Форум программистов, компьютерный форум 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
16843 / 5264 / 323
Регистрация: 30.03.2009
Сообщений: 14,159
Записей в блоге: 26
25.11.2011, 16:46     Пишем свой интерпретатор языка BASIC #421
Цитата Сообщение от Nameless One Посмотреть сообщение
очень плохо подходит в качестве языка, на основе которого нужно учиться писать компиляторы/интерпретаторы (ИМХО)
Это нам с тобой плохо подходит. А тарасу - самое то. Правда странно, что он начал не с вложенных шаблонов или лямбда-функций
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
taras atavin
Ушёл с форума.
 Аватар для taras atavin
3569 / 1752 / 91
Регистрация: 24.11.2009
Сообщений: 27,619
25.11.2011, 17:07     Пишем свой интерпретатор языка BASIC #422
Цитата Сообщение от Evg Посмотреть сообщение
Это нам с тобой плохо подходит. А тарасу - самое то. Правда странно, что он начал не с вложенных шаблонов или лямбда-функций
Издеваешься? Я не Билл и не инопланетянин. Мне точно также бейсик проще, чем плюсы. Только бейсик ещё вспоминать, а плюсы в памяти свежие. Кроме того, исходник смолбейсика у меня есть. Дойду до того места, где язык имеет значение - загляну сначала в тот исходник. А пока у меня вопросы на столько общие, что одинаково сгодятся фортран, лисп, лого, форт, вторая модула, c#, паскаль и даже рапира. Лишь бы иметь на них правильно написанные примеры программ. И начал я с
Код
int main()
{
 return 0;
}
. Спрашивается, какое именно место здесь сложнее, чем на бейсике?

Добавлено через 6 минут
Цитата Сообщение от Evg Посмотреть сообщение
Если брать язык типа простого бэйсика, которий реализовал #pragma, то каждый statement начинается с ключевого слова.
С одного и того же? Кстати, плюсы берутся только за основу, но делаю я не плюсы. И если возникнет такая необходимость, то LET, DEFINE, VARIABLE, PRINT и INPUT тоже прикручу. Только зачем?
niXman
Эксперт C++
 Аватар для niXman
3133 / 1445 / 49
Регистрация: 09.08.2009
Сообщений: 3,441
Записей в блоге: 2
25.11.2011, 17:09     Пишем свой интерпретатор языка BASIC #423
Цитата Сообщение от Nameless One Посмотреть сообщение
С++, в том виде, который мы имеем сейчас, очень плохо подходит в качестве языка, на основе которого нужно учиться писать компиляторы/интерпретаторы (ИМХО)
жуть
taras atavin
Ушёл с форума.
 Аватар для taras atavin
3569 / 1752 / 91
Регистрация: 24.11.2009
Сообщений: 27,619
25.11.2011, 17:12     Пишем свой интерпретатор языка BASIC #424
Да и на бейсике строка может начинаться с метки, причем, ранее не объявленной.
niXman
Эксперт C++
 Аватар для niXman
3133 / 1445 / 49
Регистрация: 09.08.2009
Сообщений: 3,441
Записей в блоге: 2
25.11.2011, 17:13     Пишем свой интерпретатор языка BASIC #425
taras atavin, бери исходники CLang и разбирайся. они в разы читаются проще GCC. ну или еще проще - смотри исходники tcc. но это только Си компилятор.
taras atavin
Ушёл с форума.
 Аватар для taras atavin
3569 / 1752 / 91
Регистрация: 24.11.2009
Сообщений: 27,619
25.11.2011, 17:15     Пишем свой интерпретатор языка BASIC #426
И так, вернёмся к начальному символу. Что это такое и зачем нужно?

Добавлено через 1 минуту
Цитата Сообщение от niXman Посмотреть сообщение
ери исходники CLang и разбирайся. они в разы читаются проще GCC.
Кто такой сланг?
niXman
Эксперт C++
 Аватар для niXman
3133 / 1445 / 49
Регистрация: 09.08.2009
Сообщений: 3,441
Записей в блоге: 2
25.11.2011, 17:16     Пишем свой интерпретатор языка BASIC #427
Цитата Сообщение от taras atavin Посмотреть сообщение
Кто такой сланг?
гугл забанил?
taras atavin
Ушёл с форума.
 Аватар для taras atavin
3569 / 1752 / 91
Регистрация: 24.11.2009
Сообщений: 27,619
25.11.2011, 17:25     Пишем свой интерпретатор языка BASIC #428
Ну и нафига мне с порога фреймвок разбирать? Мне бы для начала понять саму трансляцию. Поэтому или компиляция в натив, или в шитый код, причём, пока x86, или интерпретация, но исходника. И ни каких jit компиляторов байткода на целевой машине, интерпретации там же того же байткода, оптимизаторов и тому подобных прибамбасов. Возможно, что то из перечисленного будет следующим этапом после того, как заработает компилятор по проще, а пока это слишком сложно, чтоб даже заглядывать.
Nameless One
Эксперт С++
 Аватар для Nameless One
5755 / 3404 / 255
Регистрация: 08.02.2010
Сообщений: 7,393
25.11.2011, 17:29     Пишем свой интерпретатор языка BASIC #429
Цитата Сообщение от taras atavin Посмотреть сообщение
И так, вернёмся к начальному символу. Что это такое и зачем нужно?
зачем нужно:
Цитата Сообщение от taras atavin Посмотреть сообщение
начальный символ , или аксиому, с которой начинается получение любого предложения языка.
что такое:
Цитата Сообщение от Nameless One Посмотреть сообщение
один из нетерминальных символов
niXman
Эксперт C++
 Аватар для niXman
3133 / 1445 / 49
Регистрация: 09.08.2009
Сообщений: 3,441
Записей в блоге: 2
25.11.2011, 17:34     Пишем свой интерпретатор языка BASIC #430
taras atavin, хз о чем ты, но clang предоставляет результат в виде AST. ни о каких джит и байткодах речь не идет.

Добавлено через 1 минуту
Цитата Сообщение от niXman Посмотреть сообщение
предоставляет результат в виде AST.
собственно как и любой фронтенд. так что изучение нужно начинать с этого.
taras atavin
Ушёл с форума.
 Аватар для taras atavin
3569 / 1752 / 91
Регистрация: 24.11.2009
Сообщений: 27,619
25.11.2011, 17:47     Пишем свой интерпретатор языка BASIC #431
Цитата Сообщение от niXman Посмотреть сообщение
taras atavin, хз о чем ты, по clang предоставляет результат в виде AST. ни о каких джит и байткодах речь не идет.
Читай:
Clang является фронт-эндом для языков программирования C, C++, Objective-C и en:Objective-C++, использующим для оптимизации и кодогенерации фреймворк LLVM.
. И здесь:
Low Level Virtual Machine (LLVM) — универсальная система анализа, трансформации и оптимизации программ, реализующая виртуальную машину с RISC-подобными инструкциями. Может использоваться как оптимизирующий компилятор этого байткода в машинный код для различных архитектур либо для его интерпретации и JIT-компиляции (для некоторых платформ).
Добавлено через 1 минуту
И так, что такое начальный символ?
niXman
Эксперт C++
 Аватар для niXman
3133 / 1445 / 49
Регистрация: 09.08.2009
Сообщений: 3,441
Записей в блоге: 2
25.11.2011, 17:47     Пишем свой интерпретатор языка BASIC #432
taras atavin, читай:

Цитата Сообщение от taras atavin Посмотреть сообщение
Clang является фронт-эндом для языков программирования C, C++, Objective-C и en:Objective-C++
taras atavin
Ушёл с форума.
 Аватар для taras atavin
3569 / 1752 / 91
Регистрация: 24.11.2009
Сообщений: 27,619
25.11.2011, 17:50     Пишем свой интерпретатор языка BASIC #433
niXman, ты ещё до "Clang является" обруби. Дальше читай, там и про фреймвок и про про jit, и про интерпретацию байткода.
niXman
Эксперт C++
 Аватар для niXman
3133 / 1445 / 49
Регистрация: 09.08.2009
Сообщений: 3,441
Записей в блоге: 2
25.11.2011, 17:51     Пишем свой интерпретатор языка BASIC #434
Цитата Сообщение от taras atavin Посмотреть сообщение
там и про фреймвок и про про jit, и про интерпретацию байткода.

разве тебе кто-то предлагал LLVM? или ты реально не понимаешь что такое фронтэнд/мидлэнд/бэкэнд?
taras atavin
Ушёл с форума.
 Аватар для taras atavin
3569 / 1752 / 91
Регистрация: 24.11.2009
Сообщений: 27,619
25.11.2011, 17:52     Пишем свой интерпретатор языка BASIC #435
Почему нельзя получение каждого следующего предложения начинать просто по факту окончания предыдущего? Зачем нужен именно стартовый символ?

Добавлено через 44 секунды
Цитата Сообщение от niXman Посмотреть сообщение
разве тебе кто-то предлагал LLVM?
А разве нет?
niXman
Эксперт C++
 Аватар для niXman
3133 / 1445 / 49
Регистрация: 09.08.2009
Сообщений: 3,441
Записей в блоге: 2
25.11.2011, 17:53     Пишем свой интерпретатор языка BASIC #436
Цитата Сообщение от taras atavin Посмотреть сообщение
Почему нельзя получение каждого следующего предложения начинать просто по факту окончания предыдущего? Зачем нужен именно стартовый символ?
я полагаю, ты говоришь о контексте парсера.

Добавлено через 21 секунду
Цитата Сообщение от taras atavin Посмотреть сообщение
А разве нет?
пруф/цитату приведи
fasked
Эксперт C++
 Аватар для fasked
4925 / 2505 / 180
Регистрация: 07.10.2009
Сообщений: 4,306
Записей в блоге: 1
25.11.2011, 17:54     Пишем свой интерпретатор языка BASIC #437
Цитата Сообщение от niXman Посмотреть сообщение
или ты реально не понимаешь что такое фронтэнд
Я знаю! Это же тоже самое, что и фрэймворк!
Nameless One
Эксперт С++
 Аватар для Nameless One
5755 / 3404 / 255
Регистрация: 08.02.2010
Сообщений: 7,393
25.11.2011, 17:54     Пишем свой интерпретатор языка BASIC #438
Цитата Сообщение от taras atavin Посмотреть сообщение
И так, что такое начальный символ?
еще раз
если непонятно, представь, что у тебя каждый нетерминал грамматики представлен в виде функции компилятора/интерпретатора, которая принимает исходный код (или, если у тебя есть лексер, поток лексем) и возвращает, к примеру, синтаксическое дерево. Стартовому символу (стартовому нетерминалу) будет соответствовать некоторая функция. Так вот, если передать этой функции исходный код программы (или поток лексем), то она (если код корректен синтаксически) вернет тебе синтаксическое дерево, которое и будет представлять тебе программу
niXman
Эксперт C++
 Аватар для niXman
3133 / 1445 / 49
Регистрация: 09.08.2009
Сообщений: 3,441
Записей в блоге: 2
25.11.2011, 17:55     Пишем свой интерпретатор языка BASIC #439
Цитата Сообщение от fasked Посмотреть сообщение
тоже самое, что и фрэймворк!
именно! а все толковые словари врут! это всемирный заговор против taras atavin!
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
25.11.2011, 17:55     Пишем свой интерпретатор языка BASIC
Еще ссылки по теме:

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

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

Или воспользуйтесь поиском по форуму:
taras atavin
Ушёл с форума.
 Аватар для taras atavin
3569 / 1752 / 91
Регистрация: 24.11.2009
Сообщений: 27,619
25.11.2011, 17:55     Пишем свой интерпретатор языка BASIC #440
Цитата Сообщение от niXman Посмотреть сообщение
пруф/цитату приведи
Ну слаг же предлагают, а цитаты с вики я дал.
Yandex
Объявления
25.11.2011, 17:55     Пишем свой интерпретатор языка BASIC
Закрытая тема Создать тему
Опции темы

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