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

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

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

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

20.06.2009, 20:03. Просмотров 193310. Ответов 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
niXman
Эксперт С++
3139 / 1451 / 49
Регистрация: 09.08.2009
Сообщений: 3,441
Записей в блоге: 2
25.11.2011, 17:53 #436
Цитата Сообщение от taras atavin Посмотреть сообщение
Почему нельзя получение каждого следующего предложения начинать просто по факту окончания предыдущего? Зачем нужен именно стартовый символ?
я полагаю, ты говоришь о контексте парсера.

Добавлено через 21 секунду
Цитата Сообщение от taras atavin Посмотреть сообщение
А разве нет?
пруф/цитату приведи
0
fasked
Эксперт С++
4948 / 2528 / 180
Регистрация: 07.10.2009
Сообщений: 4,311
Записей в блоге: 1
25.11.2011, 17:54 #437
Цитата Сообщение от niXman Посмотреть сообщение
или ты реально не понимаешь что такое фронтэнд
Я знаю! Это же тоже самое, что и фрэймворк!
2
Nameless One
Эксперт С++
5775 / 3425 / 255
Регистрация: 08.02.2010
Сообщений: 7,448
25.11.2011, 17:54 #438
Цитата Сообщение от taras atavin Посмотреть сообщение
И так, что такое начальный символ?
еще раз
если непонятно, представь, что у тебя каждый нетерминал грамматики представлен в виде функции компилятора/интерпретатора, которая принимает исходный код (или, если у тебя есть лексер, поток лексем) и возвращает, к примеру, синтаксическое дерево. Стартовому символу (стартовому нетерминалу) будет соответствовать некоторая функция. Так вот, если передать этой функции исходный код программы (или поток лексем), то она (если код корректен синтаксически) вернет тебе синтаксическое дерево, которое и будет представлять тебе программу
0
niXman
Эксперт С++
3139 / 1451 / 49
Регистрация: 09.08.2009
Сообщений: 3,441
Записей в блоге: 2
25.11.2011, 17:55 #439
Цитата Сообщение от fasked Посмотреть сообщение
тоже самое, что и фрэймворк!
именно! а все толковые словари врут! это всемирный заговор против taras atavin!
0
taras atavin
3570 / 1753 / 91
Регистрация: 24.11.2009
Сообщений: 27,619
25.11.2011, 17:55 #440
Цитата Сообщение от niXman Посмотреть сообщение
пруф/цитату приведи
Ну слаг же предлагают, а цитаты с вики я дал.
0
niXman
Эксперт С++
3139 / 1451 / 49
Регистрация: 09.08.2009
Сообщений: 3,441
Записей в блоге: 2
25.11.2011, 17:56 #441
Цитата Сообщение от taras atavin Посмотреть сообщение
Ну слаг же предлагают, а цитаты с вики я дал.
или ты прикидываешь, или второе.
1
Nameless One
Эксперт С++
5775 / 3425 / 255
Регистрация: 08.02.2010
Сообщений: 7,448
25.11.2011, 17:57 #442
Цитата Сообщение от taras atavin Посмотреть сообщение
Почему нельзя получение каждого следующего предложения начинать просто по факту окончания предыдущего? Зачем нужен именно стартовый символ?
чтобы иметь именно входную точку парсера. Ты же не будешь (возвращаясь к моему примеру) начинать синтаксический анализ твоей программы с парсинга списка параметров функции
0
taras atavin
3570 / 1753 / 91
Регистрация: 24.11.2009
Сообщений: 27,619
25.11.2011, 17:58 #443
Цитата Сообщение от Nameless One Посмотреть сообщение
если непонятно, представь, что у тебя каждый нетерминал грамматики представлен в виде функции компилятора/интерпретатора, которая принимает исходный код (или, если у тебя есть лексер, поток лексем) и возвращает, к примеру, синтаксическое дерево. Стартовому символу (стартовому нетерминалу) будет соответствовать некоторая функция. Так вот, если передать этой функции исходный код программы (или поток лексем), то она (если код корректен синтаксически) вернет тебе синтаксическое дерево, которое и будет представлять тебе программу
Эйси. А символ то какую роль выполняет? Почему нельзя эту функцию подразумевать без всякого символа?
0
niXman
Эксперт С++
3139 / 1451 / 49
Регистрация: 09.08.2009
Сообщений: 3,441
Записей в блоге: 2
25.11.2011, 17:58 #444
Nameless One, то, о чем ты говоришь, называется контекстом парсера. я об этом несколькими постами ранее сказал. но ТС походу в танке.
0
taras atavin
3570 / 1753 / 91
Регистрация: 24.11.2009
Сообщений: 27,619
25.11.2011, 18:01 #445
Цитата Сообщение от Nameless One Посмотреть сообщение
Ты же не будешь (возвращаясь к моему примеру) начинать синтаксический анализ твоей программы с парсинга списка параметров функции
А как я на него с бухты барахты попаду? Предыдущее предложение ведь не посреди текущего закончилось.

Добавлено через 1 минуту
Цитата Сообщение от niXman Посмотреть сообщение
Nameless One, то, о чем ты говоришь, называется контекстом парсера. я об этом несколькими постами ранее сказал. но ТС походу в танке.
А что такое контекст парсера? Сам прасер вроде понятно что такое. Но что такое его контекст?
0
Nameless One
Эксперт С++
5775 / 3425 / 255
Регистрация: 08.02.2010
Сообщений: 7,448
25.11.2011, 18:04 #446
Цитата Сообщение от taras atavin Посмотреть сообщение
Предыдущее предложение ведь не посреди текущего закончилось.
какое "предыдущее"? Ключевыми словами в моем высказывании было слова "начинать" и "входная точка"
0
niXman
Эксперт С++
3139 / 1451 / 49
Регистрация: 09.08.2009
Сообщений: 3,441
Записей в блоге: 2
25.11.2011, 18:08 #447
Цитата Сообщение от taras atavin Посмотреть сообщение
А что такое контекст парсера? Сам прасер вроде понятно что такое. Но что такое его контекст?
http://ru.wikipedia.org/wiki/%D0%9A%...B8%D0%BA%D0%B0

Добавлено через 42 секунды
и в добавок: http://ru.wikipedia.org/wiki/%D0%9A%...B8%D0%BA%D0%B0
0
taras atavin
3570 / 1753 / 91
Регистрация: 24.11.2009
Сообщений: 27,619
25.11.2011, 19:53 #448
Цитата Сообщение от Nameless One Посмотреть сообщение
какое "предыдущее"? Ключевыми словами в моем высказывании было слова "начинать" и "входная точка"
То есть это первое предложение? Тогда тем более как ты сразу попадёшь на параметры?

Добавлено через 8 минут
А пример чего нибудь контекстно-зависимого для сравнения с арифметическим выражением?
0
silent_1991
Эксперт С++
4987 / 3044 / 149
Регистрация: 11.11.2009
Сообщений: 7,027
Завершенные тесты: 1
25.11.2011, 21:00 #449
Цитата Сообщение от taras atavin Посмотреть сообщение
То есть это первое предложение?
Да, это своего рода базис, к нему в конечном итоге должно "свернуться" корректное предложение грамматики.
0
taras atavin
3570 / 1753 / 91
Регистрация: 24.11.2009
Сообщений: 27,619
26.11.2011, 07:39 #450
Я кажется понял, о чём речь. О слове
Pascal
1
program
. Первое предложение должно начинаться с начала файла.
0
26.11.2011, 07:39
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
26.11.2011, 07:39
Привет! Вот еще темы с ответами:

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

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

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

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


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

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

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