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

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

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

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

20.06.2009, 20:03. Просмотров 191674. Ответов 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
17934 / 6162 / 409
Регистрация: 30.03.2009
Сообщений: 16,917
Записей в блоге: 27
23.12.2009, 21:52 #301
Кстати, давно хотел сказать, но всё время забывал. Тот тест, который ты у меня взял, он у тебя коряво работает.

Вариант для моего интерпретатора

PureBasic
1
2
3
4
5
6
7
8
9
10
11
SCREEN 1
 
FOR FI=0.0 TO 639
  LET X1 = FI
  LET Y1 = 240
  LET X2 = 240 + 150.0 * SIN (FI/60)
  LET Y2 = 240 + 150.0 * COS (FI/60)
  LINE (X1,Y1) - (X2,Y2)
NEXT FI
 
SLEEP 3
Вариант для твоего интерпретатора

PureBasic
1
2
3
4
5
6
7
8
9
SCREEN 12
 
FOR FI=0 TO 639
  LET X1 = FI
  LET Y1 = 240
  LET X2 = 240 + 150 * SIN (FI/60)
  LET Y2 = 240 + 150 * COS (FI/60)
  LINE (X1,Y1) - (X2,Y2), 13
NEXT FI
Мой и твой результаты на скриншотах. Если заметишь - я в свой вариант воткнул именно плавающие значение, т.к. изначально неправильно сказал тебе о правилах неявных преобразований (мне казалось, что второй операнд всегда приводится к типу первого, а на самом деле более "узкий" приводится к более "широкому"). Правда от твоего интерпретатора я не смог добиться такой же картинки
1
Миниатюры
Пишем свой интерпретатор языка BASIC   Пишем свой интерпретатор языка BASIC  
AnonymC
1162 / 444 / 23
Регистрация: 23.06.2009
Сообщений: 6,279
Завершенные тесты: 1
23.12.2009, 22:33 #302
если что надо помогу с Хеллоу ворлд!
1
RazorQ
577 / 344 / 9
Регистрация: 06.02.2009
Сообщений: 1,386
26.12.2009, 17:57 #303
Я в голове прокручиваю мысли по поводу дебагера и не могу представить себе этот процесс даже в теории. В чем я уверен, так это в том, что не нужно изобретать велосипед, т.е. писать свой интерпретатор (я как минимум морально не готов писать). С другой стороны каким образом отслеживать ход программы во время интерпретации? Отдавать на съедение basin по строчке кода? А как быть с циклами и ветвлениями? Может есть какая-то поддержка со стороны самого интерпретатора? Так же я пока не знаю, откуда мне можно будет получить список переменных и как обновлять их значения.
1
#pragma
Временно недоступен
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
26.12.2009, 18:22  [ТС] #304
Я так думаю,что опираться мы будем на пользовательскую отладочную печать.Некое подобие уже реализовано в виде опций командной строки --trace-act,--trace-tree,--trace-silent,а также опций в самом коде с помощью инструкции OPTION. Проблема,я так понимаю,в остановке внутри самого интерпретатора по событию,зависящему от пользователя. Я вижу такое простое решение (хотя Evg наверняка его забракует) - в командную строку перед запуском интерпретатора подаются номера строк для остановки,а также имена нужных переменных для просмотра по какому-то согласованному формату.В этом случае интерпретатор будет останавливаться на этих строках в исходнике,параллельно печатая в stdout значения нужных переменных,и ожидая ввода пользователя (любую клавишу или enter).А уже IDE будет отлавливать эти значения,и показывать их,где нужно.

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

Ну это дебаггер встроенный,а как делать внешний,я пока не знаю
1
RazorQ
577 / 344 / 9
Регистрация: 06.02.2009
Сообщений: 1,386
26.12.2009, 18:39 #305
Хорошо. Всё же интерпретатор должен быть чистым (ИМХО), т.е. без встроенного отладчика. Но хотелось бы послушать, что скажет Evg, а я пока отложу написание дебагера.
1
Evg
Эксперт CАвтор FAQ
17934 / 6162 / 409
Регистрация: 30.03.2009
Сообщений: 16,917
Записей в блоге: 27
26.12.2009, 19:04 #306
В случае компилятора отладчик возможно делать внешним, потому как по результату работы компилятора генерится независимый (от компилятора) исполняемый файл. В этот файл складывается отладочная информация, по которой отладчик может найти соотвествие между кодом (данными) и текстом исходной программы.

В случае интерпретатора отладчик может быть только встроенным, потому как независимо выполнять программу нет возможности. Как это выглядит внешне - могу попробовать расписать. Но вот вопрос - вы когда-нибудь работали с отладчиком gdb? Внешне должно выглядеть точно так же. За счёт того, что отладчик встроен в интерпретатор, то снимается более 90% геморроя, связанного с отладкой независимого приложения

Добавлено через 9 минут
В кратце выглядит примерно так. Запускается интерпретатор со специальной опцией, по которой интерпретатор начинает работать в интерактивном режиме. Т.е. он ожидает ввода команд с консоли. Пользователь пишет в управляющей консоли "break source.bas:30", что означет поставить брейкпоинт в файле source.bas в строке 30. Далее пользователь пишет "run", что означает запуск программы. По достижении указанного места интерпретатор перестаёт исполнять программу и опять ожидает ввода. Пользователь пишет "print A", интерпретатор распечатывает значение переменной "A". Ползтватель пишет "next", интерпретатор исполняет один оператор и опять вываливается в командную строку. И т.п. IDE должна делать по большому счёту то же самое. Когда пользователь жмёт мышкой на какую-то строку, то IDE должна отправить в интерпретатор команду "break ...". Когда пользователь жмёт правой кнопкой мышки над именем переменной и выбирает "Распечатать значение", то IDE отправляет интерпреатору приказ "print ..." и перехватывает результат. По такому же принципу работают все графические отладчики: есть некий низкоуровневый отладчик, а повер него строится графическая оболочка
2
RazorQ
577 / 344 / 9
Регистрация: 06.02.2009
Сообщений: 1,386
26.12.2009, 19:05 #307
Цитата Сообщение от Evg Посмотреть сообщение
Но вот вопрос - вы когда-нибудь работали с отладчиком gdb?
Постоянно. Из выше сказанного могу сделать вывод, что мне нужно всего лишь наладить обмен данными между интерпретатором и IDE. Только вот как? Сейчас я предполагаю, что все что выводиться в stdout это идет на консоль для пользователя, а то что выводиться в stderr - для программы.

Добавлено через 1 минуту
Всё понял, спасибо
1
Evg
Эксперт CАвтор FAQ
17934 / 6162 / 409
Регистрация: 30.03.2009
Сообщений: 16,917
Записей в блоге: 27
26.12.2009, 19:18 #308
Цитата Сообщение от RazorQ Посмотреть сообщение
Постоянно. Из выше сказанного могу сделать вывод, что мне нужно всего лишь наладить обмен данными между интерпретатором и IDE. Только вот как? Сейчас я предполагаю, что все что выводиться в stdout это идет на консоль для пользователя, а то что выводиться в stderr - для программы.
Скажем так, интерпретаторов я никогда не писал, но пока в теории выглядит именно так. Всегда можно сделать дополнительную опцию (означающую запуск из-под IDE), а по этой опции весь вывод интерпретатора будет делаться под IDE, а не под человека (что упростит в IDE синтаксический разбор всей этой писанины, а так же появится свобода вы выдаче сообщений пользователю без боязни того, что это сломает работу IDE)

Как вариант вместо stdout, stderr можно открывать дополнительные файловые потоки, опять-таки чтобы не путать печати программы с выводом интерпретатора
1
RazorQ
577 / 344 / 9
Регистрация: 06.02.2009
Сообщений: 1,386
27.12.2009, 16:17 #309
Слушайте, а как называется ваш язык программирования? Я не стал перечитывать всю тему, может вы уже говорили об этом. Просто я всегда называю его Basic (именно так, а не BASIC), может я ошибаюсь.
1
Evg
Эксперт CАвтор FAQ
17934 / 6162 / 409
Регистрация: 30.03.2009
Сообщений: 16,917
Записей в блоге: 27
27.12.2009, 18:03 #310
BASIC расшифровывается что-то типа "the Beginners All-sourpose Symbolic Instruction Code". Т.е. авторы взяли слово "basic" и сделали из него аббревиатуру. Поэтому провально писать все буквы заглавными. Диалектов бэйсика существует целая куча (грубо говоря, каждый разработчик делал немного по-своему). За основу мы брали Q-BASIC.
1
RazorQ
577 / 344 / 9
Регистрация: 06.02.2009
Сообщений: 1,386
27.12.2009, 18:36 #311
Выкладываю наработки. Версия не совсем в рабочем состоянии, но я уже выбился из сил искать ошибку! Прошу откомпилировать, запустить и выключить. А потом рассказать мне о впечатлениях.
1
Вложения
Тип файла: zip Basin-IDE.zip (109.8 Кб, 28 просмотров)
Evg
Эксперт CАвтор FAQ
17934 / 6162 / 409
Регистрация: 30.03.2009
Сообщений: 16,917
Записей в блоге: 27
27.12.2009, 20:57 #312
У меня пока отсутсвует линукс
1
#pragma
Временно недоступен
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
28.12.2009, 07:05  [ТС] #313
Почему-то у меня не скомпилировалось.
Листинг ошибок (после того,как выполнил qmake,а затем make)
Код
$ make
/usr/bin/uic-qt4 mainwindow.ui -o ui_mainwindow.h
/usr/bin/uic-qt4 finddialog.ui -o ui_finddialog.h
Warning: name layoutWidget is already used
g++ -c -pipe -O2 -Wall -W -D_REENTRANT -DQT_NO_DEBUG -DQT_GUI_LIB -DQT_CORE_LIB -DQT_SHARED -I/usr/share/qt4/mkspecs/linux-g++ -I. -I/usr/include/qt4/QtCore -I/usr/include/qt4/QtGui -I/usr/include/qt4 -I. -I. -I. -o highlighter.o highlighter.cpp
g++ -c -pipe -O2 -Wall -W -D_REENTRANT -DQT_NO_DEBUG -DQT_GUI_LIB -DQT_CORE_LIB -DQT_SHARED -I/usr/share/qt4/mkspecs/linux-g++ -I. -I/usr/include/qt4/QtCore -I/usr/include/qt4/QtGui -I/usr/include/qt4 -I. -I. -I. -o main.o main.cpp
In file included from mainwindow.h:9,
                 from main.cpp:5:
ui_mainwindow.h: In member function ‘void Ui_MainWindow::setupUi(QMainWindow*)’:
ui_mainwindow.h:88: error: ‘class QAction’ has no member named ‘setPriority’
ui_mainwindow.h:88: error: ‘NormalPriority’ is not a member of ‘QAction’
make: *** [main.o] Error 1
0
RazorQ
577 / 344 / 9
Регистрация: 06.02.2009
Сообщений: 1,386
28.12.2009, 10:09 #314
Почему-то у меня не скомпилировалось.
Открываем mainwindow.ui в дизайнере -> выделяем кнопочку "Новый" или в списке действий выбираем newAct -> свойство priority устанавливаем по умолчанию (кнопочка похожая на Enter в свойствах)
0
#pragma
Временно недоступен
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
28.12.2009, 14:50  [ТС] #315
А можно как-то без дизайнера обойтись? Я просто хочу скомпилить проект в консоли.. Ты скажи,что в исходнике поменять,я поменяю,или патч сделай,а то я что-то не въеду с этими кнопочками.
0
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
28.12.2009, 14:50
Привет! Вот еще темы с ответами:

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

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

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

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


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

Или воспользуйтесь поиском по форуму:
315
Yandex
Объявления
28.12.2009, 14:50
Закрытая тема Создать тему
Опции темы

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