Форум программистов, компьютерный форум 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)
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Ensase
Флудер
 Аватар для Ensase
191 / 29 / 5
Регистрация: 23.03.2007
Сообщений: 335
20.06.2009, 20:06     Пишем свой интерпретатор языка BASIC #2
вот сейчас открыл книгу - нет там никакой опечатки, всё верно. это у вас была опечатка
а неявное преобразование для кода ошибки вполне приемлемо
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
21.06.2009, 12:26  [ТС]     Пишем свой интерпретатор языка BASIC #3
Значит,у вас версия книги новее))
CheshireCat
Эксперт С++
2907 / 1235 / 78
Регистрация: 27.05.2008
Сообщений: 3,307
21.06.2009, 12:30     Пишем свой интерпретатор языка BASIC #4
Странный какой-то дебаггер..... Если условие (1) выполнилось, происходит не переход к switch, а выход из функции get_token() с возвратом значения END.
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
21.06.2009, 12:32  [ТС]     Пишем свой интерпретатор языка BASIC #5
Да,я уже понял,что ступил)
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16824 / 5245 / 319
Регистрация: 30.03.2009
Сообщений: 14,121
Записей в блоге: 26
21.06.2009, 21:58     Пишем свой интерпретатор языка BASIC #6
#pragma, а ты все задания просто передираешь из книги или всё-таки что-то пытаешься сделать сам? Вопрос задаю НЕ с целью, чтобы сказать "ай-ай-ай". Просто ты уже кучу вопросов вских задавал. Если ты действительно решил серьёзно занаться программированием, то постараюсь отвечать ещё более развёрнуто. Если интерес несерьёзный - то как обычно
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
21.06.2009, 23:27  [ТС]     Пишем свой интерпретатор языка BASIC #7
Цитата Сообщение от Evg Посмотреть сообщение
#pragma, а ты все задания просто передираешь из книги или всё-таки что-то пытаешься сделать сам? Вопрос задаю НЕ с целью, чтобы сказать "ай-ай-ай". Просто ты уже кучу вопросов вских задавал. Если ты действительно решил серьёзно занаться программированием, то постараюсь отвечать ещё более развёрнуто. Если интерес несерьёзный - то как обычно
Конечно,пытаюсь делать сам(в этом примере первым порывом было написать вообще по-своему),просто такое задание в книге(я учусь по ней и стараюсь выполнить все задания для начала) - заставьте работать данный код калькулятора,ну что я могу сделать? )) Просто в данном конкретном случае я немного не дотянул до решения,надо было ещё немного поковырять код до понимания(буду стараться впредь).Я даже специальную тему поднял,типа почему нельзя писать [решено] в первом сообщении темы,чтобы другие не тратили на это время.Согласен,некоторые мои вопросы из разряда тех,что "вот тут недодумал,вот тут недонапрягся",но некоторые действительно требуют хороших ответов.Я учусь сам,посоветоваться не с кем,надеюсь только на лучшее,и очень ценю любую помощь,особенно помощь настоящих хакеров.Интерес серьёзный,хочу достигнуть хорошего уровня.Не только в С++,но в программировании вообще.
P.S.Твоё "как обычно" тоже круто для меня )) но мотаю на ус всё сказанное и написанное)
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16824 / 5245 / 319
Регистрация: 30.03.2009
Сообщений: 14,121
Записей в блоге: 26
22.06.2009, 12:06     Пишем свой интерпретатор языка BASIC #8
> но мотаю на ус всё сказанное и написанное)

Ну вот это самое главное. Остальное со временем придёт

Добавлено через 12 часов 27 минут 44 секунды
Наверное в страуструпе есть и дальнейшее развитие этой задачи. Со своей стороны могу дыть 3 предложения по дальнейшему апгрейду
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
22.06.2009, 22:28  [ТС]     Пишем свой интерпретатор языка BASIC #9
Цитата Сообщение от Evg Посмотреть сообщение
Наверное в страуструпе есть и дальнейшее развитие этой задачи. Со своей стороны могу дыть 3 предложения по дальнейшему апгрейду
Пиши.
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16824 / 5245 / 319
Регистрация: 30.03.2009
Сообщений: 14,121
Записей в блоге: 26
22.06.2009, 23:44     Пишем свой интерпретатор языка BASIC #10
По возрастающей сложности (как мне кажется)

1. Работать со входным файлом (т.е. фактически получается некий простенький интерпретатор). Выдавать пользовательские ошибки с прявязкой к исходнику: т.е. печатать имя файла, номер строки, номер позиции в строке (последнее некритично), суть ошибки. Использование неинициализированной переменной считать ошибкой. При этом добавить конструкцию "print <var_name>". На вход программы подаём имя файла с текстом программы, которую будем интерпретировать

2. Заменить плавающий типа (double) на целочисленный и добавить операции битовой арифметики: & | ~ << >>. Приоритет операций такой же, как и в си (можешь посмотреть в том страуструпе).

3. Сделать типизацию. Т.е. понимать как плавающие типы, так и целые. При первой записи в перменную, её тип вычисляется по типу выражения, стоящей в правой части присваивания. Т.е. для "a = 5" переменная a заводится как целочисленная, для "a = 5.0" - как плавающая. Все дальнейшие присваивания в переменную делаются с учётом типа. Т.е. если изначально переменная была как целочисленная, то операция "a = 23.2" означает запись целого значения 23 (потому как a целое).

При вычислении двухоперандногй операции выражения, второй операнд должен приводиться к типу первого операнда. Т.е. "12 + 34.5" должно вычисляться как (пишу в терминах си) "12 + (int)34.5", а "12.1 + 7" должно вычисляться как "12.1 + (float)7". При попытке построения битовых операций над плавающим типом выдавать ошибку (ибо считаем, что битовые типы разрешены только для целых чисел)

Может быть эти задания тебе покажутся сложными, но всё же рекомендую сделать все три. Потому что это тебе сильно поможет в освоении проектирования программ. Если от этого не устанешь, а меня не заломает - попробуем дальше это дело наворотить. Таким образом, писать программу следует с учётом того, что её функциональность будет расширяться - это ещё добавит экспы в части проектирования. Да и на своих же ошибках, возможно, начнёшь лучше понимать, как НЕ надо делать
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
25.06.2009, 19:10  [ТС]     Пишем свой интерпретатор языка BASIC #11
Итак,после длительного биения об стену ,можно подвести предварительный итог:
1)Программа читает входной файл,но как она это делает,я понятия не имею,потому что делал по описанию библиотеки из и-нета http://www.cplusplus.com
2)Есть некое подобие привязки к исходнику,есть сообщение об ошибке,но иногда работает некорректно из-за этих каскадных вызовов...
3)Я попытался добавить возможность программируемых пользователем функций,но в самом конце выяснилось,что это не работет,опять же из-за подобия рекурсии.Так как анализ посимвольный,после знака $ должно происходить вычисление,но при последующем вызове expr(true) происходит дальнейшее чтение строки и в итоге ошибка.
4)В функции get_resulting_expr (она ещё не доделана) ошибка в алгоритме(не заменяется последний параметр).
5)Задания твои я попытаюсь сделать,просто я ещё,например,понятия не имею о битовых операциях.
6)В-общем,пока не знаю,как всё это разгребать.Писалось всё это с целью просто сделать,хотя бы сделать,а не как сделать...На данный момент есть код:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#ifndef LEXICALANALYZER_H_INCLUDED
#define LEXICALANALYZER_H_INCLUDED
 
#include <cctype>
#include <string>
#include <map>
#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
#include <fstream>
 
 
extern    std::ifstream source;
 
enum Token_value {
    NAME,       NUMBER,      END,       FUNC = '$',
    PLUS = '+', MINUS = '-', MUL = '*', DIV = '/',
    PRINT = ';',ASSIGN = '=',LP = '(',  RP = ')',
    BEGIN_FUNC = '@',                END_FUNC = '|'
};
 
typedef std::map<std::string,int>Map_of_names;
 
extern Token_value curr_tok;
extern std::map<std::string,double>table;
extern std::string string_value;
extern std::map<std::string,int>func_name;
extern std::vector<std::string>func_value;
extern std::map<std::string,int>func_num_of_param;
extern std::string get_resulting_expr(std::string,std::string,std::string,int);
extern int no_of_errors;
extern int string_number;
 
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
#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 /= d;
                     break;
                 }
                 return error("Деление на ноль");
            default:
                 return left;
        }
}
 
double number_value;
std::string string_value;
std::map<std::string,int>func_name;
std::vector<std::string>func_value;
std::map<std::string,int>func_name_to_param;
std::ifstream source;
int string_number=0;
 
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 FUNC:{
            double e = expr(true);
            return e;
        }
        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 (!source.get(ch)) return curr_tok = END;
 
    } while (ch!='\n'&&isspace(ch));
 
    switch (ch) {
        case 0:
                 return curr_tok = END;
 
        case ';':
                 return curr_tok = PRINT;
        case '\n':
                 ++string_number;
                 return curr_tok = PRINT;
 
        case '*':case '/':case '+':case '-':case '(':case ')':
        case '=':
                 return curr_tok=Token_value(ch);
 
        case '0':case '1':case '2':case '3':case '4' :
        case '5':case '6':case '7':case '8':case '9':case '.':
                 source.putback(ch);
                 source>>number_value;
                 return curr_tok = NUMBER;
 
        case '@':{
                 // Writing whole string to build a function.
                 // @ - reserved sign for start user function
                 // programming.
 
                 // Copying whole function string to tmp
                 std::string tmp;
                 tmp = ch;
                 while (source.get(ch)&&ch!='\n') tmp.push_back(ch);
                 source.putback(ch);
 
                 // Set useful places in the string
                 unsigned int i = find(tmp.begin(),tmp.end(),'(')-tmp.begin();
                 unsigned int k = find(tmp.begin(),tmp.end(),')')-tmp.begin();
 
                 // There must be parameters list in the function
                 if (i==tmp.size()||k==tmp.size())
                 {
                     error("Ожидался список параметров");
                     return curr_tok = PRINT;
                 }
 
                 // Checking function name for previous declaration
                 std::string s = tmp.substr(1,i-1);
                 Map_of_names::iterator it = ::func_name.find(s);
                 if (it != ::func_name.end())
                 {
                     error("Повторное определение функции");
                     return curr_tok = PRINT;
                 }
                 else
                 {
                    // Declaration
                    unsigned int j = find(tmp.begin(),tmp.end(),'{')-tmp.begin();
 
                    // Function must have body
                    if (j==tmp.size())
                    {
                        error("Ожидалась {");
                        return curr_tok = PRINT;
                    }
 
                    // Not empty body
                    if (tmp.substr(j+1,tmp.size()-j-2).size()<1)
                    {
                        error("Пустая функция");
                        return curr_tok = PRINT;
                    }
 
                    // At start of parameters list,as at the end,
                    // have to be an alphabet character
                    std::string param_list = tmp.substr(i+1,k-i-1);
                    if (isalpha(param_list.at(0))&&isalpha(param_list[param_list.size()-1]))
                    {
                        // Function template creation
                        ::func_name_to_param[s] = count(param_list.begin(),param_list.end(),',')+1;
                        ::func_name[s];
                        ::func_value.push_back(tmp.substr(i,tmp.size()-i));
                        //std::cout<<::func_value[0]<<std::endl;
                    }
                    else
                        error("Неправильный список параметров");
                        return curr_tok = PRINT;
                 }
                 return curr_tok = PRINT;
             }
        case '}':{
                 // End of user function
                 return curr_tok = PRINT;
             }
        case '$':{
                 // $ - reserved for starting to calculate
                 // function that was programmed before
 
                 // Writing function with parameters
                 // that we need to calculate to tmp
                 std::string tmp;
                 tmp = ch;
 
                 while (source.get(ch)&&(ch != '\n')) tmp.push_back(ch);
                 if (ch=='\n') ++string_number;
                 source.putback(ch);
 
                 // Set useful places in the string
                 unsigned int i = find(tmp.begin(),tmp.end(),'(')-tmp.begin();
                 unsigned int k = find(tmp.begin(),tmp.end(),')')-tmp.begin();
 
                 // Checking for function was declared before
                 std::string nm_tmp = tmp.substr(1,i-1);
                 Map_of_names::iterator it = ::func_name.find(nm_tmp);
                 if (it == ::func_name.end())
                 {
                     error("Функция не была объявлена ранее");
                     return curr_tok = PRINT;
                 }
                 else
                 {
                     std::string param_list = tmp.substr(i+1,k-i-1);
                     if (isalnum(param_list[0])&&isalnum(param_list[param_list.size()-1]))
                     {
                         // Count parameters and checking amount of
                         // parameters in declared function
                         int param_count = count(param_list.begin(),param_list.end(),',')+1;
                         if (param_count> ::func_name_to_param[nm_tmp]||
                             param_count< ::func_name_to_param[nm_tmp])
                         {
                             error("Неправильное количество параметров функции");
                             return curr_tok = PRINT;
                         }
                         ///////////////////////////////////////////
                         /**  Our function is "OK".               */
                         ///////////////////////////////////////////
                         std::string main_param_list = ::func_value[::func_name[nm_tmp]].substr(1,
                                                                                         ::func_value[::func_name[nm_tmp]].find_first_of(')',1)-1);
 
                         std::string result_expr = get_resulting_expr(param_list,
                                                                      main_param_list,
                                                                      ::func_value[::func_name[nm_tmp]],
                                                                      ::func_name_to_param[nm_tmp]);
                         result_expr.push_back('\n');
 
                         for (int j = result_expr.size()-1; j >= 0; --j)
                         source.putback(result_expr[j]);
                         return  curr_tok = FUNC;
 
                     }
                     else
                     {
                         error("Неверный параметр функции");
                         return curr_tok = PRINT;
                     }
                 }
                 return curr_tok = FUNC;
             }
        default:
             if (isalpha(ch)) {
                 ::string_value = ch;
                 while (source.get(ch)&&isalnum(ch)) ::string_value.push_back(ch);
                 source.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<<"Строка: "<<string_number<<" Ошибка: "<<s<<'\n';
    return 0;
}

param_swapper.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
#include "LexicalAnalyzer.h"
 
// Function get parameters and temlate in
// strings of two functions and returns
// resulting string as expression for
// future calculation
// Просто жесть
 
std::string get_resulting_expr(std::string s_son_par,
                               std::string s_father_par,
                               std::string s_father_expr,
                               int param_count)
{
    std::string resulting_expr;
    if (param_count==1)
    {
        //To be continued?
    }
    else
    {
        // Store parameters
        std::vector<std::string>tmp_son_par;
        std::vector<std::string>tmp_father_par;
 
        // Using iterators to find parameters
        std::string::iterator iter_s_son = find(s_son_par.begin(),s_son_par.end(),',');
        std::string::iterator iter_s_father = find(s_father_par.begin(),s_father_par.end(),',');
 
        // First parameter
        tmp_son_par.push_back(s_son_par.substr(0,(iter_s_son-s_son_par.begin())));
        tmp_father_par.push_back(s_father_par.substr(0,(iter_s_father-s_father_par.begin())));
 
        if (param_count > 2)
        {
           // All parameters exept first and last
           for (int i = 1; i < param_count-1; ++i)
           {
               iter_s_son = find(iter_s_son,s_son_par.end(),',');
               iter_s_father = find(iter_s_father,s_father_par.end(),',');
               tmp_son_par.push_back(s_son_par.substr(iter_s_son-s_son_par.begin(),
                                                 (find(iter_s_son,s_son_par.end(),',')-iter_s_son)-1));
               tmp_father_par.push_back(s_father_par.substr(iter_s_father-s_father_par.begin(),
                                                 (find(iter_s_father,s_father_par.end(),',')-iter_s_father)-1));
           }
 
           // Last parameter
           tmp_son_par.push_back(s_son_par.substr(iter_s_son-s_son_par.begin(),
                                                  iter_s_son-s_son_par.end()));
           tmp_father_par.push_back(s_father_par.substr(iter_s_father-s_father_par.begin(),
                                                        iter_s_father-s_father_par.end()));
        }
        if (param_count == 2)
        {
           // Second parameter
           tmp_son_par.push_back(s_son_par.substr(iter_s_son-s_son_par.begin(),
                                                  iter_s_son-s_son_par.end()));
 
           tmp_father_par.push_back(s_father_par.substr(iter_s_father-s_father_par.begin(),
                                                        iter_s_father-s_father_par.end()));
        }
 
        // Finally replace parameters from son
        // to father expression
        unsigned int pos   = 0;
        unsigned int l=s_father_expr.size();
        for (int i = 0; i < param_count;++i)
        {
            for (pos = 0;pos < l;)
            {
                // There is a mistake in algorithm.
                // Last parameter isnt getting swapped.
                pos=s_father_expr.find(tmp_father_par.at(i));
                if (pos>l)break;
                s_father_expr.replace(pos,tmp_father_par.at(i).size(),tmp_son_par.at(i));
 
            std::cout<<s_father_expr<<std::endl;
            }
        }
        // Using now useless pos
        pos = s_father_expr.find_first_of('{',0);
        resulting_expr = s_father_expr.substr(pos+1,s_father_expr.size()-pos-2);
    }
    std::cout<<resulting_expr<<std::endl;
    return resulting_expr;
}

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

Входные данные в файле program.clc
program.clc
C++
1
2
3
4
5
a=1
b=2
a+b
@f(c,d){c+d*7}
$f(a,b)
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16824 / 5245 / 319
Регистрация: 30.03.2009
Сообщений: 14,121
Записей в блоге: 26
25.06.2009, 19:55     Пишем свой интерпретатор языка BASIC #12
А можешь скомпилить бинарник, а то в этих чёртовых билдарах я не пойму, как консольное приложение сделать. Чисто интересно посотмреть

Цитата Сообщение от #pragma Посмотреть сообщение
1)Программа читает входной файл,но как она это делает,я понятия не имею,потому что делал по описанию библиотеки из и-нета http://www.cplusplus.com
Для порядка разберись. В идеале надо это самому ручками написать, чтобы всё-таки понимать, что же там делается

Цитата Сообщение от #pragma Посмотреть сообщение
2)Есть некое подобие привязки к исходнику,есть сообщение об ошибке,но иногда работает некорректно из-за этих каскадных вызовов...
Вроде бы мелочь, но в результате надо тоже добиться нормальной работы. Как показывает практика, человеческий пользовательский интерфейс зачастую сделать куда труднее, чем внутреннюю логику.

Цитата Сообщение от #pragma Посмотреть сообщение
3)Я попытался добавить возможность программируемых пользователем функций,но в самом конце выяснилось,что это не работет,опять же из-за подобия рекурсии.Так как анализ посимвольный,после знака $ должно происходить вычисление,но при последующем вызове expr(true) происходит дальнейшее чтение строки и в итоге ошибка.
Я бы на твоём месте выхов функций пока отложил. Оно слишком геморно, чтобы аккуратно сделать. Я понятия не имею, как оно у тебя работает, но сильно подозреваю, что у тебя в месте вызова фактически подставляется тело функции. Т.е. вызов по сути нечестный (рекурсию таким образом не сделаешь)

Цитата Сообщение от #pragma Посмотреть сообщение
5)Задания твои я попытаюсь сделать,просто я ещё,например,понятия не имею о битовых операциях.
Суть была не втом, чтобы сделать битовые функции, а втом, чтобы добавить ещё один уровень приоритета. Чтобы понять, насколько хорошо ты усвоил разбор выражений. Ну и вторая часть смысла битовых операций - они только целые, т.е. уметь запрещать класс операций для какого-то типа данных (в данном случае запретить битовые операции для плавающих чисел)

Цитата Сообщение от #pragma Посмотреть сообщение
6)В-общем,пока не знаю,как всё это разгребать.Писалось всё это с целью просто сделать,хотя бы сделать,а не как сделать...На данный момент есть код:
А ты не спеши. И разгребай по-тихоньку. Чем дольше ты будешь сидеть над программой, тем лучше начнёшь её "чувствовать". А начиная с какого-то момента появится стойкое ощущение, что какие-то места надо переделать. В этом случае не ленись и переделай

Добавлено через 1 минуту 52 секунды
Я собственно задание то со следующей целью дал. Вот ты сделал код по книге. А задание - это чтобы доработки сделать самому и без книги
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
25.06.2009, 21:10  [ТС]     Пишем свой интерпретатор языка BASIC #13
Ладно,функции пока оставлю,буду потихоньку дальше продвигаться.Бинарник прилагается.
Вложения
Тип файла: zip calculator.zip (86.9 Кб, 273 просмотров)
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16824 / 5245 / 319
Регистрация: 30.03.2009
Сообщений: 14,121
Записей в блоге: 26
25.06.2009, 21:43     Пишем свой интерпретатор языка BASIC #14
А сам Exe'шник где? Если Calculator нужно просто переименовать в *.exe, то у меня он что-то завис

Добавлено через 1 минуту 0 секунд
Пишет "Program too big to fit in memoty"

Добавлено через 3 минуты 53 секунды
Под линухом скомпилялось и запустилось. Щас вот только разберусь, как русские сообщения нормально печатать

Добавлено через 10 минут 58 секунд
Когда я делал поставновку задачи, для первого пункта я имел в виду следующее. Ты имеешь файл

Код
a = 1
b = 2
c = a + b
print c
Т.е. печатать только то, что захочет пользователь. А ещё поддержать комментарии. Чтобы всякие разности в файле оставлять и каждый раз заново не писать. Кстати, после ошибки надо завершать работу, а не исполнять дальше

А так для начала очень даже неплохо. Правда пока, насколько я понимаю, тут в основном чужой код, но, тем не менее, начало положено. Я исходники не смотрел, только запускал программу. Даже деление на ноль отловилось. Из ошибок отловил только неправильную реакцию на "a=2.2.2"

Добавлено через 2 минуты 31 секунду
Но использование неинициализированной переменной нужно запретить. Скорее для тренировки в отработке ошибочных ситуаций, чем для практики (т.к. большинство интерпретируемых языков позволяет использовать неинициализированные переменные)
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
28.06.2009, 02:43  [ТС]     Пишем свой интерпретатор языка BASIC #15
1) Кое-что удалось сделать.Добавил битовые операции &,^,~,|,но способ,которым это сделано,мне не нравится.Вообще,вся эта программа-тихий ужас,не дай бог вот над такими программами придётся когда-нибудь сидеть и разбираться)) Кстати,результат проверить не мешало бы,но я поглядел по аналогии (в отдельной программе),вроде бы корректно.

2) Добавил вывод переменной на экран по желанию пользователя.Это делается командой #<var name> .Я до сих пор не додумался(мало думал?),как сделать интерпретацию нескольких символов подряд,"не теряя при этом нити беседы" - то есть чтобы предотвратить вызов посимвольно во время интерпретации зарезервированных слов.Как-то я поставил на все 300 с лишним строк программы точки разрыва для дебаггера,и у меня получилось,что,например,при попытке вычисления функции (case $: )после записи её тела в строку и возврате строка продолжает читаться,но в предыдущем вызове.Короче там такая путаница,ё-маё Аж дебаггер заглючило.

3)Добавил возможность комментариев,это делается значком "(двойной апостроф),всё,что после значка,игнорируется.

4)Есть ограничение на битовые операции с числами с плавающей точкой.Типизацию пока не сделал.Просто хочется сделать наподобие "int x=1;" но всё упирается в проблему,описанную ранее.

5) Запретил использование неинициализированных переменных,а также программа прерывается после первой ошибки.

6)На данный момент программа такова:
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
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
#ifndef LEXICALANALYZER_H_INCLUDED
#define LEXICALANALYZER_H_INCLUDED
 
#include <cctype>
#include <string>
#include <map>
#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
#include <fstream>
 
 
extern std::ifstream source;
 
enum Token_value {
    NAME,       NUMBER,      END,       FUNC = '$',
    PLUS = '+', MINUS = '-', MUL = '*', DIV = '/',
    PRINT = ';',ASSIGN = '=',LP = '(',  RP = ')',
    AND = '&',  XOR = '^',   OR = '|',  NOT = '~',
    BEGIN_FUNC = '@',COMMENT = '"'
};
 
enum Printer_state {
    PRINT_VAR,DONT_PRINT_VAR
};
 
enum Error_state {
    BIN_TO_DOUBLE,NO_ERRORS,
    GLOBAL_ERROR
};
 
typedef std::map<std::string,int>Map_of_names;
typedef std::map<std::string,double>Map_of_table;
 
extern Token_value curr_tok;
extern Error_state err_state;
extern Printer_state print_var;
 
extern std::map<std::string,double>table;
extern std::vector<std::string>initialized_vars;
extern std::string string_value;
 
extern std::map<std::string,int>func_name;
extern std::vector<std::string>func_value;
extern std::map<std::string,int>func_num_of_param;
extern std::string result_expr;
extern std::string get_resulting_expr(std::string,std::string,std::string,int);
 
extern int string_number;
 
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
#include "LexicalAnalyzer.h"
 
 
std::map<std::string,double>table;
Token_value curr_tok = PRINT;
Error_state err_state = NO_ERRORS;
Printer_state print_var = DONT_PRINT_VAR;
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 /= d;
                     break;
                 }
                 err_state=GLOBAL_ERROR;
                 return error("Деление на ноль");
            case AND:
                 if (err_state != BIN_TO_DOUBLE)
                 {
                     left = static_cast<int>(left) & static_cast<int>(prim(true));
                     break;
                 }
                 err_state = GLOBAL_ERROR;
                 return error("Битовые операции могут выполняться только над целыми типами");
            case XOR:
                 if (err_state != BIN_TO_DOUBLE)
                 {
                     left = static_cast<int>(left) ^ static_cast<int>(prim(true));
                     break;
                 }
                 err_state = GLOBAL_ERROR;
                 return error("Битовые операции могут выполняться только над целыми типами");
            case OR:
                 if (err_state != BIN_TO_DOUBLE)
                 {
                     left = static_cast<int>(left) | static_cast<int>(prim(true));
                     break;
                 }
                 err_state = GLOBAL_ERROR;
                 return error("Битовые операции могут выполняться только над целыми типами");
            default:
                 if (err_state != BIN_TO_DOUBLE)
                 return left;
                 err_state = GLOBAL_ERROR;
                 return error("Битовые операции могут выполняться только над целыми типами");
        }
}
 
double number_value;
std::string string_value;
std::map<std::string,int>func_name;
std::vector<std::string>func_value;
std::map<std::string,int>func_name_to_param;
std::vector<std::string>initialized_vars;
std::string result_expr;
std::ifstream source;
int string_number=0;
 
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);
                        ::initialized_vars.push_back(::string_value);
                        return v;
                    }
                    else
                    {
                        std::vector<std::string>::iterator i = find(::initialized_vars.begin(),
                                                                    ::initialized_vars.end(),
                                                                    ::string_value);
                        if (i == ::initialized_vars.end())
                        {
                            err_state=GLOBAL_ERROR;
                            return error("Неинициализированная переменная");
                        }
                    }
                    return v;
        }
        case FUNC:{
                   double e = expr(true);
                   return e;
        }
        case MINUS:
                   return -prim(true);
        case NOT:{
                   if (err_state != BIN_TO_DOUBLE)
                   {
                       int e = ~static_cast<int>(prim(true));
                       if (err_state == BIN_TO_DOUBLE)
                       {
                            err_state = GLOBAL_ERROR;
                            return error("Битовые операции могут выполняться только над целыми типами");
                       }
                       return static_cast<double>(e);
                   }
                   else
                   {
                       err_state = GLOBAL_ERROR;
                       return error("Битовые операции могут выполняться только над целыми типами");
                   }
        }
        case LP:{
                  double e = expr(true);
                  if (curr_tok!=RP)
                  {
                      err_state = GLOBAL_ERROR;
                      return error("Ожидалась )");
                  }
                  get_token();
                  return e;
        }
        case COMMENT:
                     get_token();
                     return 0;
        default:
                  err_state = GLOBAL_ERROR;
                  return error("Ожидалось первичное выражение");
    }
}
 
Token_value get_token()
{
    char ch = 0;
 
 
    if (curr_tok == COMMENT)
        do {
               if (!source.get(ch))
               return curr_tok = END;
 
           }
        while (ch!='\n');
 
    if (curr_tok == FUNC)
        do {
               if (!source.get(ch))
               return curr_tok = END;
           }
        while (ch!='\n');
 
    do {
        if (!source.get(ch)) return curr_tok = END;
 
    } while (ch!='\n'&&isspace(ch));
 
    switch (ch) {
        case 0:
                 return curr_tok = END;
 
        case ';':
                 return curr_tok = PRINT;
 
        case '\n':
                 ++string_number;
                 return curr_tok = PRINT;
 
        case '"':return curr_tok =COMMENT;
 
        case '*':case '/':case '+':case '-':case '(':case ')':
        case '=':case '~':case '&':case '^':case '|':
                 return curr_tok=Token_value(ch);
 
        case '0':case '1':case '2':case '3':case '4' :
        case '5':case '6':case '7':case '8':case '9':
                 source.putback(ch);
                 source>>number_value;
                 if(number_value-static_cast<int>(number_value)!=0)
                 err_state = BIN_TO_DOUBLE;
                 return curr_tok = NUMBER;
        case '@':{
                 // Writing whole string to build a function.
                 // @ - reserved sign for start user function
                 // programming.
 
                 // Copying whole function string to tmp
                 std::string tmp;
                 tmp = ch;
                 while (source.get(ch)&&ch!='\n') tmp.push_back(ch);
                 source.putback(ch);
 
                 // Set useful places in the string
                 unsigned int i = find(tmp.begin(),tmp.end(),'(')-tmp.begin();
                 unsigned int k = find(tmp.begin(),tmp.end(),')')-tmp.begin();
 
                 // There must be parameters list in the function
                 if (i==tmp.size()||k==tmp.size())
                 {
                     err_state = GLOBAL_ERROR;
                     error("Ожидался список параметров");
                     return curr_tok = PRINT;
                 }
 
                 // Checking function name for previous declaration
                 std::string s = tmp.substr(1,i-1);
                 Map_of_names::iterator it = ::func_name.find(s);
                 if (it != ::func_name.end())
                 {
                     err_state = GLOBAL_ERROR;
                     error("Повторное определение функции");
                     return curr_tok = PRINT;
                 }
                 else
                 {
                    // Declaration
                    unsigned int j = find(tmp.begin(),tmp.end(),'{')-tmp.begin();
 
                    // Function must have body
                    if (j==tmp.size())
                    {
                        err_state = GLOBAL_ERROR;
                        error("Ожидалась {");
                        return curr_tok = PRINT;
                    }
 
                    // Not empty body
                    if (tmp.substr(j+1,tmp.size()-j-2).size()<1)
                    {
                        err_state = GLOBAL_ERROR;
                        error("Пустая функция");
                        return curr_tok = PRINT;
                    }
 
                    // At start of parameters list,as at the end,
                    // have to be an alphabet character
                    std::string param_list = tmp.substr(i+1,k-i-1);
                    if (isalpha(param_list.at(0))&&isalpha(param_list[param_list.size()-1]))
                    {
                        // Function template creation
                        ::func_name_to_param[s] = count(param_list.begin(),param_list.end(),',')+1;
                        ::func_name[s];
                        ::func_value.push_back(tmp.substr(i,tmp.size()-i));
                        return curr_tok = PRINT;
                    }
                    else
                    {
                        err_state = GLOBAL_ERROR;
                        error("Неправильный список параметров");
                        return curr_tok = PRINT;
                    }
                 }
                 return curr_tok = PRINT;
             }
        case '}':case '{':{
                 // End of user function
                 source.putback(ch);
                 return curr_tok = PRINT;
             }
        case ',':return curr_tok = PRINT;
        case '#':
                 print_var = PRINT_VAR;
                 return curr_tok = PRINT;
 
        case '$':{
                 // $ - reserved for starting to calculate
                 // function that was programmed before
 
                 // Writing function with parameters
                 // that we need to calculate to tmp
                 std::string tmp;
                 tmp = ch;
 
                 while (source.get(ch)&&(ch != '\n')) tmp.push_back(ch);
                 if (ch=='\n') ++string_number;
                 source.putback(ch);
 
                 // Set useful places in the string
                 unsigned int i = find(tmp.begin(),tmp.end(),'(')-tmp.begin();
                 unsigned int k = find(tmp.begin(),tmp.end(),')')-tmp.begin();
 
                 // Checking for function was declared before
                 std::string nm_tmp = tmp.substr(1,i-1);
                 Map_of_names::iterator it = ::func_name.find(nm_tmp);
                 if (it == ::func_name.end())
                 {
                     err_state = GLOBAL_ERROR;
                     error("Функция не была объявлена ранее");
                     return curr_tok = PRINT;
                 }
                 else
                 {
                     std::string param_list = tmp.substr(i+1,k-i-1);
                     if (isalnum(param_list[0])&&isalnum(param_list[param_list.size()-1]))
                     {
                         // Count parameters and checking amount of
                         // parameters in declared function
                         int param_count = count(param_list.begin(),param_list.end(),',')+1;
                         if (param_count> ::func_name_to_param[nm_tmp]||
                             param_count< ::func_name_to_param[nm_tmp])
                         {
                             err_state = GLOBAL_ERROR;
                             error("Неправильное количество параметров функции");
                             return curr_tok = PRINT;
                         }
                         ///////////////////////////////////////////
                         /**  Our function is "OK".               */
                         ///////////////////////////////////////////
                         std::string main_param_list = ::func_value[::func_name[nm_tmp]].substr(1,
                                                                                         ::func_value[::func_name[nm_tmp]].find_first_of(')',1)-1);
 
                         ::result_expr = get_resulting_expr(param_list,
                                                            main_param_list,
                                                            ::func_value[::func_name[nm_tmp]],
                                                            ::func_name_to_param[nm_tmp]);
                         //::result_expr.push_back('\n');
 
                         for (int j = ::result_expr.size()-1; j >= 0; --j)
                         source.putback(::result_expr[j]);
                         return  curr_tok = FUNC;
 
                     }
                     else
                     {
                         err_state = GLOBAL_ERROR;
                         error("Неверный параметр функции");
                         return curr_tok = PRINT;
                     }
                 }
                 return curr_tok = PRINT;
             }
        default:
             if (isalpha(ch)) {
                 ::string_value = ch;
                 while (source.get(ch)&&isalnum(ch)) ::string_value.push_back(ch);
                 source.putback(ch);
                 return curr_tok = NAME;
             }
             err_state = GLOBAL_ERROR;
             error ("Неправильная лексема");
             return curr_tok = PRINT;
    }
}
int error (const std::string& s)
{
    std::cerr<<"Строка: "<< ::string_number<<" Ошибка: "<<s<<'\n';
    return 0;
}

param_swapper.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
#include "LexicalAnalyzer.h"
 
// Function get parameters and temlate in
// strings of two functions and returns
// resulting string as expression for
// future calculation
// Просто жесть
 
std::string get_resulting_expr(std::string s_son_par,
                               std::string s_father_par,
                               std::string s_father_expr,
                               int param_count)
{
    std::string resulting_expr;
    if (param_count==1)
    {
        //To be continued?
    }
    else
    {
        // Store parameters
        std::vector<std::string>tmp_son_par;
        std::vector<std::string>tmp_father_par;
 
        // Using iterators to find parameters
        std::string::iterator iter_s_son = find(s_son_par.begin(),s_son_par.end(),',');
        std::string::iterator iter_s_father = find(s_father_par.begin(),s_father_par.end(),',');
 
        // First parameter
        tmp_son_par.push_back(s_son_par.substr(0,(iter_s_son-s_son_par.begin())));
        tmp_father_par.push_back(s_father_par.substr(0,(iter_s_father-s_father_par.begin())));
 
        if (param_count > 2)
        {
           // All parameters exept first and last
           for (int i = 1; i < param_count-1; ++i)
           {
               iter_s_son = find(iter_s_son,s_son_par.end(),',');
               iter_s_father = find(iter_s_father,s_father_par.end(),',');
               tmp_son_par.push_back(s_son_par.substr(iter_s_son-s_son_par.begin(),
                                                 (find(iter_s_son,s_son_par.end(),',')-iter_s_son)-1));
               tmp_father_par.push_back(s_father_par.substr(iter_s_father-s_father_par.begin(),
                                                 (find(iter_s_father,s_father_par.end(),',')-iter_s_father)-1));
           }
 
           // Last parameter
           tmp_son_par.push_back(s_son_par.substr(iter_s_son-s_son_par.begin(),
                                                  iter_s_son-s_son_par.end()));
           tmp_father_par.push_back(s_father_par.substr(iter_s_father-s_father_par.begin(),
                                                        iter_s_father-s_father_par.end()));
        }
        if (param_count == 2)
        {
           // Second parameter
           tmp_son_par.push_back(s_son_par.substr(iter_s_son-s_son_par.begin(),
                                                  iter_s_son-s_son_par.end()));
 
           tmp_father_par.push_back(s_father_par.substr(iter_s_father-s_father_par.begin(),
                                                        iter_s_father-s_father_par.end()));
        }
 
        // Finally replace parameters from son
        // to father expression
        unsigned int pos   = 0;
        unsigned int l = s_father_expr.size();
        for (int i = 0; i < param_count;++i)
        {
            for (pos = 0;pos <s_father_expr.size();)
            {
                // There is a mistake in algorithm.
                // Last parameter isnt getting swapped.
                pos = s_father_expr.find(tmp_father_par.at(i));
                if (pos>l)break;
                s_father_expr.replace(pos,tmp_father_par.at(i).size(),tmp_son_par.at(i));
 
            std::cout<<"DEBUG "<<s_father_expr<<std::endl;
            }
        }
        // Using now useless pos
        pos = s_father_expr.find_first_of('{',0);
        resulting_expr = s_father_expr.substr(pos+1,s_father_expr.size()-pos-2);
    }
    std::cout<<"DEBUG "<<resulting_expr<<std::endl;
    return resulting_expr;
}

main.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
#include "LexicalAnalyzer.h"
 
 
int main()
{
    table["pi"]=3.1415926535897932385;
    table["e"]=2.7182818284590452354;
    source.open("program.clc",std::ifstream::in);
    while (source.good()) {
        get_token();
        if (err_state==GLOBAL_ERROR)break;
        if (curr_tok==END) break;
        if (curr_tok==PRINT) continue;
        if (print_var==PRINT_VAR)
        {
           std::cout<<expr(false)<<'\n';
           print_var = DONT_PRINT_VAR;
        }
        else
        {
            expr(false);
        }
    }
    source.close();
    return 0;
}

Входные данные:
program.clc
#~0 "Печатаем дополнение нуля
a=3
b=4
"This is comment
"Это комментарий
#a "Ура!Каменты! : )
"Это тоже комментарий
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16824 / 5245 / 319
Регистрация: 30.03.2009
Сообщений: 14,121
Записей в блоге: 26
28.06.2009, 12:08     Пишем свой интерпретатор языка BASIC #16
Код
a=10
b=11
c=12
d=13
e=a+b|c*d
#e
Код
Строка: 6 Ошибка: Неинициализированная переменная
0
1. Исправить ошибку (текст корректный, но интерпретатор отработал неправильно)
2. Когда пишешь "неинициализированная переменная", надо ещё указывать, какая переменная. Написано, что строка 6 "#e", я могу догадаться, что это e. Но если бы ошибка была на строке 5, а там выражение из тридцати слагаемых, то надо долго разбираться, что же там неинициализировано.
3. В случае ошибки программа по прежнему отрабатывает до конца. Советую этот вопрос долго не откладывать, а отработать для себя механизм выдачи пользовательской ошибки, потому как чем раньше ты это сделаешь, тем меньше в будущем надо будет переделывать

Цитата Сообщение от #pragma Посмотреть сообщение
но способ,которым это сделано,мне не нравится.Вообще,вся эта программа-тихий ужас,не дай бог вот над такими программами придётся когда-нибудь сидеть и разбираться))
Когда доведёшь до более-менее логически завершённого состояния, не поленись переписать свои коды по-человечески. Только работающий исходник при этом законсервируй, чтобы всегда его можно было восстановить. Можешь просто rar'ом закатать. В идеале - положить под управление системы контроля версий (типа svn), но сейчас если ты в это полезешь, можешь много времени потерять на то, чтобы разобраться. Правда потерянное время себя оправдает

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

Цитата Сообщение от #pragma Посмотреть сообщение
Я до сих пор не додумался(мало думал?),как сделать интерпретацию нескольких символов подряд,"не теряя при этом нити беседы" - то есть чтобы предотвратить вызов посимвольно во время интерпретации зарезервированных слов.
Поясни на конкретном примере, а то в общих словах я ничего не понял

Цитата Сообщение от #pragma Посмотреть сообщение
4)Есть ограничение на битовые операции с числами с плавающей точкой
Следующий код отрабатывает без ошибок (хотя там число с плавающей точкой)

Код
a = 10.0 | 11

Если вдруг ты ещё не понял, я хочу протолкнуть твою программу до интерпретатора бэйсика. Конечно же полноценный бэйсик не получится, но некое его примитивное подобие - а почему бы и нет? Поэтому я сначала хочу от тебя добиться стабильной работы твоей программы в простейших случаях (запись в переменные, их чтение и печать, выдача ошибок). Дальше попробуем навести некий структурный порядок в том, что ты сделал, а потом попробуем продвинуться дальше.

Я специально продивгаю по чуть-чуть. Чтобы с появлением каждых новых фич тебе приходилось что-то в программе серьёзно переделывать. Только так можно научиться правильно строить программу с точки зрения её архитектуры. А мозг устроен так, что сначала нужно много раз сделать неправильно, чтобы чётко понимать в дальнейшем, как же нужно делать правильно

Добавлено через 2 минуты 58 секунд
А код по процедурным вызовам удали пока, чтобы не мешал. Ибо это большой паравоз, прицепленный к твоей программе, который ты пока не знаешь как работает - он только занимает место и мозолит глаза

Добавлено через 10 минут 40 секунд
Кстати, положи к себе в проект ещё и текстовый файл, в который будешь складывать все найденные ошибки. А исправленные ошибки переносить в другой файл (но не удалять - для истории полезно их сохранить)
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
29.06.2009, 01:02  [ТС]     Пишем свой интерпретатор языка BASIC #17
Хотел уточнить свои слова насчёт проблемы с вызовом функций(ну очень хочется доделать)
Цитата Сообщение от Evg Посмотреть сообщение
Поясни на конкретном примере, а то в общих словах я ничего не понял
В-общем как я понимаю в данном конкретном случае проблема сводится к тому,как правильно послать готовую строку (которую я уже приготовил для вычисления,например "a*b") обратно в поток ввода.То есть у меня получается что когда из функции get_token() возвращается значение FUNC,за этим следует вызов expr(true) с последующими вызовами get_token(),но в потоке для чтения нет строки,которую я приготовил и отослал функцией putback в строке 342: source.putback(::result_expr[j]); В итоге при прилагаемых входных данных программа печатает 'a',которая следует за именем функции и находится в списке параметров,а моя готовая строка ещё до этого куда-то девается..Вот я думаю может я неправильно это сделал(в смысле работал с потоком)?
Вот прилагаю исходник,если не лень поглядеть,конечно.А насчёт svn это в интернете репозиторий создается,навроде как в launchpad сделано?Бесспорно,это удобнее...есть ещё cvs какой-то.
main.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
#include "LexicalAnalyzer.h"
#include "error_handler.h"
 
 
int main()
{
    table["pi"]=3.1415926535897932385;
    table["e"]=2.7182818284590452354;
    source.open("program.clc",std::ifstream::in);
    while (source.good()) {
        get_token();
        if (err_state==GLOBAL_ERROR)break;
        if (curr_tok==END) break;
        if (curr_tok==PRINT) continue;
        double x=expr(false);
        if (print_var==PRINT_VAR)
        {
           std::cout<<x<<'\n';
           print_var = DONT_PRINT_VAR;
        }
    }
    source.close();
    return 0;
}

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
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
#ifndef LEXICALANALYZER_H_INCLUDED
#define LEXICALANALYZER_H_INCLUDED
 
#include <cctype>
#include <string>
#include <map>
#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
#include <fstream>
 
extern std::ifstream source;
 
enum Token_value {
    NAME,       NUMBER,      END,       FUNC = '$',
    PLUS = '+', MINUS = '-', MUL = '*', DIV = '/',
    PRINT = ';',ASSIGN = '=',LP = '(',  RP = ')',
    AND = '&',  XOR = '^',   OR = '|',  NOT = '~',
    BEGIN_FUNC = '@',COMMENT = '"'
};
 
enum Printer_state {
    PRINT_VAR,DONT_PRINT_VAR
};
 
 
 
typedef std::map<std::string,int>Map_of_names;
typedef std::map<std::string,double>Map_of_table;
 
extern Token_value curr_tok;
extern Printer_state print_var;
 
extern std::map<std::string,double>table;
extern std::vector<std::string>initialized_vars;
extern std::string string_value;
 
extern std::map<std::string,int>func_name;
extern std::vector<std::string>func_value;
extern std::map<std::string,int>func_num_of_param;
extern std::string result_expr;
extern std::string get_resulting_expr(std::string,std::string,std::string,int);
 
 
Token_value get_token();
 
double expr(bool);
double term (bool);
double prim (bool);
 
 
 
#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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
#include "LexicalAnalyzer.h"
#include "error_handler.h"
 
 
std::map<std::string,double>table;
Token_value curr_tok = PRINT;
Printer_state print_var = DONT_PRINT_VAR;
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 /= d;
                     break;
                 }
                 return error(divide_by_zero);
            case AND:
                 if (err_state != BIN_TO_DOUBLE)
                 {
                     left = static_cast<int>(left) & static_cast<int>(prim(true));
                     break;
                 }
                 return error(binary_to_double);
            case XOR:
                 if (err_state != BIN_TO_DOUBLE)
                 {
                     left = static_cast<int>(left) ^ static_cast<int>(prim(true));
                     break;
                 }
                 return error(binary_to_double);
            case OR:
                 if (err_state != BIN_TO_DOUBLE)
                 {
                     left = static_cast<int>(left) | static_cast<int>(prim(true));
                     break;
                 }
                 return error(binary_to_double);
            default:
                 return left;
        }
}
 
double number_value;
std::string string_value;
std::map<std::string,int>func_name;
std::vector<std::string>func_value;
std::map<std::string,int>func_name_to_param;
std::vector<std::string>initialized_vars;
std::string result_expr;
std::ifstream source;
int string_number=0;
 
double prim (bool get)
{
    if (get) get_token();
    switch (curr_tok){
 
        case NUMBER:{
                    double& v = ::number_value;
                    get_token();
                    if (curr_tok==LP)
                        return error(wrong_expression);
                    return v;
        }
        case NAME:{
                    double& v = table[::string_value];
                    if (get_token()==ASSIGN)
                    {
                        ::initialized_vars.push_back(::string_value);
                        v = expr(true);
                        return v;
                    }
                    else
                    {
                        int i = count(::initialized_vars.begin(),::initialized_vars.end(),::string_value);
                        if (i == 0)
                        {
                            table.erase(::string_value);
                            return error(unknown_variable,::string_value);
                        }
                    }
                    return v;
        }
        case MINUS:
                   return -prim(true);
        case NOT:{
                   if (err_state != BIN_TO_DOUBLE)
                   {
                       int e = ~static_cast<int>(prim(true));
                       if (err_state == BIN_TO_DOUBLE)
                       {
                            return error(binary_to_double);
                       }
                       return static_cast<double>(e);
                   }
                   else
                       return error(binary_to_double);
        }
        case LP:{
                   double e = expr(true);
                   if (curr_tok!=RP)
                       return error(lp_expected);
                   get_token();
                   return e;
        }
        case FUNC:{
                   double e = expr(true);
                   return e;
        }
        case COMMENT:
                     get_token();
                     return 0;
        default:
                  return error(primary_expr_expected);
    }
}
 
Token_value get_token()
{
    char ch = 0;
 
 
    if (curr_tok == COMMENT)
        do {
               if (!source.get(ch))
               return curr_tok = END;
 
           }
        while (ch!='\n');
    /*if (curr_tok == FUNC)
        do {
               if (!source.get(ch))
               return curr_tok = END;
 
           }
        while (ch!='\n');*/
    do {
        if (!source.get(ch)) return curr_tok = END;
 
    } while (ch!='\n'&&isspace(ch));
 
    switch (ch) {
        case 0:
                 return curr_tok = END;
 
        case ';':
                 return curr_tok = PRINT;
 
        case '\n':
                 ++string_number;
                 return curr_tok = PRINT;
 
        case '"':return curr_tok =COMMENT;
 
        case '*':case '/':case '+':case '-':case '(':case ')':
        case '=':case '~':case '&':case '^':case '|':
                 return curr_tok=Token_value(ch);
 
        case '0':case '1':case '2':case '3':case '4' :
        case '5':case '6':case '7':case '8':case '9':
                 source.putback(ch);
                 source>>number_value;
                 if(number_value-static_cast<int>(number_value)!=0)
                 err_state = BIN_TO_DOUBLE;
                 return curr_tok = NUMBER;
 
        case ',':return curr_tok = PRINT;
 
        case '#':
                 print_var = PRINT_VAR;
                 return curr_tok = PRINT;
 
        case '@':{
                 // Writing whole string to build a function.
                 // @ - reserved sign for start user function
                 // programming.
 
                 // Copying whole function string to tmp
                 std::string tmp;
                 tmp = ch;
                 while (source.get(ch)&&ch!='\n') tmp.push_back(ch);
                 source.putback(ch);
 
                 // Set useful places in the string
                 unsigned int i = find(tmp.begin(),tmp.end(),'(')-tmp.begin();
                 unsigned int k = find(tmp.begin(),tmp.end(),')')-tmp.begin();
 
                 // There must be parameters list in the function
                 if (i==tmp.size()||k==tmp.size())
                 {
                     error("Ожидался список параметров");
                     return curr_tok = PRINT;
                 }
 
                 // Checking function name for previous declaration
                 std::string s = tmp.substr(1,i-1);
                 Map_of_names::iterator it = ::func_name.find(s);
                 if (it != ::func_name.end())
                 {
                     error("Повторное определение функции");
                     return curr_tok = PRINT;
                 }
                 else
                 {
                    // Declaration
                    unsigned int j = find(tmp.begin(),tmp.end(),'{')-tmp.begin();
 
                    // Function must have body
                    if (j==tmp.size())
                    {
                        error("Ожидалась {");
                        return curr_tok = PRINT;
                    }
 
                    // Not empty body
                    if (tmp.substr(j+1,tmp.size()-j-2).size()<1)
                    {
                        error("Пустая функция");
                        return curr_tok = PRINT;
                    }
 
                    // At start of parameters list,as at the end,
                    // have to be an alphabet character
                    std::string param_list = tmp.substr(i+1,k-i-1);
                    if (isalpha(param_list.at(0))&&isalpha(param_list[param_list.size()-1]))
                    {
                        // Function template creation
                        ::func_name_to_param[s] = count(param_list.begin(),param_list.end(),',')+1;
                        ::func_name[s];
                        ::func_value.push_back(tmp.substr(i,tmp.size()-i));
                        return curr_tok = PRINT;
                    }
                    else
                    {
                        error("Неправильный список параметров");
                        return curr_tok = PRINT;
                    }
                 }
                 return curr_tok = PRINT;
             }
        case '}':case '{':{
                 // End of user function
                 source.putback(ch);
                 return curr_tok = PRINT;
             }
        case '$':{
                 // $ - reserved for starting to calculate
                 // function that was programmed before
 
                 // Writing function with parameters
                 // that we need to calculate to tmp
                 std::string tmp;
                 tmp = ch;
 
                 while (source.get(ch)&&(ch != '\n')) tmp.push_back(ch);
                 source.putback(ch);
 
                 // Set useful places in the string
                 unsigned int i = find(tmp.begin(),tmp.end(),'(')-tmp.begin();
                 unsigned int k = find(tmp.begin(),tmp.end(),')')-tmp.begin();
 
                 // Checking for function was declared before
                 std::string nm_tmp = tmp.substr(1,i-1);
                 Map_of_names::iterator it = ::func_name.find(nm_tmp);
                 if (it == ::func_name.end())
                 {
                     error("Функция не была объявлена ранее");
                     return curr_tok = PRINT;
                 }
                 else
                 {
                     std::string param_list = tmp.substr(i+1,k-i-1);
                     if (isalnum(param_list[0])&&isalnum(param_list[param_list.size()-1]))
                     {
                         // Count parameters and checking amount of
                         // parameters in declared function
                         int param_count = count(param_list.begin(),param_list.end(),',')+1;
                         if (param_count> ::func_name_to_param[nm_tmp]||
                             param_count< ::func_name_to_param[nm_tmp])
                         {
                             error("Неправильное количество параметров функции");
                             return curr_tok = PRINT;
                         }
                         ///////////////////////////////////////////
                         /**  Our function is "OK".               */
                         ///////////////////////////////////////////
                         std::string main_param_list = ::func_value[::func_name[nm_tmp]].substr(1,
                                                                                         ::func_value[::func_name[nm_tmp]].find_first_of(')',1)-1);
 
                         ::result_expr = get_resulting_expr(param_list,
                                                            main_param_list,
                                                            ::func_value[::func_name[nm_tmp]],
                                                            ::func_name_to_param[nm_tmp]);
                        // ::result_expr.push_back('\n');
 
                         for (int j = ::result_expr.size(); j >= 0; --j)
                         source.putback(::result_expr[j]);
                         return  curr_tok = FUNC;
 
                     }
                     else
                     {
                         error("Неверный параметр функции");
                         return curr_tok = PRINT;
                     }
                 }
                 return curr_tok = PRINT;
             }
        default:
             if (isalpha(ch)) {
                 ::string_value = ch;
                 while (source.get(ch)&&isalnum(ch)) ::string_value.push_back(ch);
                 source.putback(ch);
                 return curr_tok = NAME;
             }
             error (wrong_expression);
             return curr_tok = PRINT;
    }
}

error_handler.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
27
#ifndef ERROR_HANDLER_H_INCLUDED
#define ERROR_HANDLER_H_INCLUDED
 
#include <string>
#include <iostream>
 
enum Error_state {
    BIN_TO_DOUBLE,NO_ERRORS,
    GLOBAL_ERROR
};
 
extern Error_state err_state;
 
 
const std::string divide_by_zero        = "Деление на ноль";
const std::string binary_to_double      = "Битовые операции могут выполняться только над целыми типами";
const std::string unknown_variable      = "Неинициализированная переменная";
const std::string lp_expected           = "Ожидалась )";
const std::string primary_expr_expected = "Ожидалось первичное выражение";
const std::string wrong_expression      = "Неправильная лексема";
 
extern int string_number;
 
int error(const std::string&);
int error(const std::string,const std::string);
 
#endif // ERROR_HANDLER_H_INCLUDED

error_handler.cpp
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include "LexicalAnalyzer.h"
#include "error_handler.h"
 
Error_state err_state = NO_ERRORS;
 
int error(const std::string& s)
{
    ++string_number;
    ::err_state=GLOBAL_ERROR;
    ::print_var = DONT_PRINT_VAR;
    std::cerr<<"Строка: "<< ::string_number<<" Ошибка: "<<s<<'\n';
    return 0;
}
int error(const std::string s,const std::string v)
{
    ++string_number;
    err_state=GLOBAL_ERROR;
    print_var = DONT_PRINT_VAR;
    std::cerr<<"Строка: "<< ::string_number
             <<" Ошибка: "<<s<<' '<<"'"<<v<<"'"<<'\n';
    return 0;
}

param_swapper.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
#include "LexicalAnalyzer.h"
 
// Function get parameters and temlate in
// strings of two functions and returns
// resulting string as expression for
// future calculation
// Просто жесть
 
std::string get_resulting_expr(std::string s_son_par,
                               std::string s_father_par,
                               std::string s_father_expr,
                               int param_count)
{
    std::string resulting_expr;
    if (param_count==1)
    {
        //To be continued?
    }
    else
    {
        // Store parameters
        std::vector<std::string>tmp_son_par;
        std::vector<std::string>tmp_father_par;
 
        // Using iterators to find parameters
        std::string::iterator iter_s_son = find(s_son_par.begin(),s_son_par.end(),',');
        std::string::iterator iter_s_father = find(s_father_par.begin(),s_father_par.end(),',');
 
        // First parameter
        tmp_son_par.push_back(s_son_par.substr(0,(iter_s_son-s_son_par.begin())));
        tmp_father_par.push_back(s_father_par.substr(0,(iter_s_father-s_father_par.begin())));
 
        if (param_count > 2)
        {
           // All parameters exept first and last
           for (int i = 1; i < param_count-1; ++i)
           {
               iter_s_son = find(iter_s_son,s_son_par.end(),',');
               iter_s_father = find(iter_s_father,s_father_par.end(),',');
               tmp_son_par.push_back(s_son_par.substr(iter_s_son-s_son_par.begin(),
                                                 (find(iter_s_son,s_son_par.end(),',')-iter_s_son)-1));
               tmp_father_par.push_back(s_father_par.substr(iter_s_father-s_father_par.begin(),
                                                 (find(iter_s_father,s_father_par.end(),',')-iter_s_father)-1));
           }
 
           // Last parameter
           tmp_son_par.push_back(s_son_par.substr(iter_s_son-s_son_par.begin(),
                                                  iter_s_son-s_son_par.end()));
           tmp_father_par.push_back(s_father_par.substr(iter_s_father-s_father_par.begin(),
                                                        iter_s_father-s_father_par.end()));
        }
        if (param_count == 2)
        {
           // Second parameter
           tmp_son_par.push_back(s_son_par.substr(iter_s_son-s_son_par.begin(),
                                                  iter_s_son-s_son_par.end()));
 
           tmp_father_par.push_back(s_father_par.substr(iter_s_father-s_father_par.begin(),
                                                        iter_s_father-s_father_par.end()));
        }
 
        // Finally replace parameters from son
        // to father expression
        unsigned int pos   = 0;
        unsigned int l = s_father_expr.size();
        for (int i = 0; i < param_count;++i)
        {
            for (pos = 0;pos <s_father_expr.size();)
            {
                // There is a mistake in algorithm.
                // Last parameter isnt getting swapped.
                pos = s_father_expr.find(tmp_father_par.at(i));
                if (pos>l)break;
                s_father_expr.replace(pos,tmp_father_par.at(i).size(),tmp_son_par.at(i));
 
            std::cout<<"DEBUG "<<s_father_expr<<std::endl;
            }
        }
        // Using now useless pos
        pos = s_father_expr.find_first_of('{',0);
        resulting_expr = s_father_expr.substr(pos+1,s_father_expr.size()-pos-2);
    }
    std::cout<<"DEBUG "<<resulting_expr<<std::endl;
    return resulting_expr;
}

Входной файл:
program.clc
a=10.6
b=2
@f(c,d){c*b}
#$f(a,b)
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16824 / 5245 / 319
Регистрация: 30.03.2009
Сообщений: 14,121
Записей в блоге: 26
29.06.2009, 20:53     Пишем свой интерпретатор языка BASIC #18
Цитата Сообщение от #pragma Посмотреть сообщение
В-общем как я понимаю в данном конкретном случае проблема сводится к тому,как правильно послать готовую строку (которую я уже приготовил для вычисления,например "a*b") обратно в поток ввода.То есть у меня получается что когда из функции get_token() возвращается значение FUNC,за этим следует вызов expr(true) с последующими вызовами get_token(),но в потоке для чтения нет строки,которую я приготовил и отослал функцией putback в строке 342: source.putback(::result_expr[j]); В итоге при прилагаемых входных данных программа печатает 'a',которая следует за именем функции и находится в списке параметров,а моя готовая строка ещё до этого куда-то девается..Вот я думаю может я неправильно это сделал(в смысле работал с потоком)?
Видишь ли, такой подход он неправильный. Лексический разбор в таких простых случаях должен идти без putback'ов. Т.е. разобрал имя функции, разобрал левую скобку, дальше с текущей позиции запускается разбор выражения для параметра. Именно идёт разбор, а не формируется строка. Далее по выходу из процедуры разбора выражения ты будешь иметь уже вычисленное значение параметра, адальше тебе останется разобрать правую скобку, после чего сделать вызов. При этом в интерпретируемых языках, содержащих процедуры, как правило строится сначало некое промежуточное представление, а затем работа идёт на нём (без входного потока). Я, собственно, потому и предлагал тебе пока забить на вызов функций, потому как надо сначала до конца добить простые вещи. Но я тебе не навязываю своё мнение, если есть желание сделать функцию - попробуй

Цитата Сообщение от #pragma Посмотреть сообщение
Вот прилагаю исходник,если не лень поглядеть,конечно.
Я мельком пробегал. Моя идея состояла в том, чтобы довести до рабочего состояния то, что уже сделано. Затем привести в порядок структуру программы. Уже на текущий момент в программе имеется как минимум три логические единицы: грамматический анализатор, лексический анализатор и таблица переменных. И всё это пока свалено в одну большую кучу.

Исходники погляжу дома. Но, вижу, уже обработка ошибок аккуратно выделена в отдельный модуль, т.е. процесс идёт и появляется понимание того, как надо делать аккуратную разбивку на отдельные независимые блоки

Цитата Сообщение от #pragma Посмотреть сообщение
А насчёт svn это в интернете репозиторий создается,навроде как в launchpad сделано?Бесспорно,это удобнее...есть ещё cvs какой-то.
Можно и на локальной машине Я поначалу делал просто на файловой системе и доступ через файловую систему. А потом переделал на сетевой вариант. В итоге у меня svn сервер запущен под виндой, под vmwar'ой запущен линух и через виртуальную сеть из-под линуха работаю с svn-репозиторием, который физически находится под виндой

Добавлено через 10 часов 14 минут 45 секунд
Глубоко не смотрел, но поглядел поверхностно исходники. По теущему положению тебе действительно будет сложно дальше развивать интерпретатор. Со своей стороны могу тебе предожить для начала навести порядок. Затем чётко разбить на компоненты (потому как у тебя пока мешанина): аккуратно сделать механизм обработки ошибок, грамматический анализатор, лексический анализатор, разбиение на операторы (statement), коих у тебя пока работающих только два (оператор присваивания и оператор печати), таблицу переменных. Затем добавлять поддержку новых конструкций.

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

Если есть какие-то идеи, вопросы и т.п. - не стесняйся, спрашивай. Пока я добрый, помогу

Добавлено через 12 минут 53 секунды
Для такого примера не ловит использование неинициализированной переменной b во 2-й строке
Код
a=10.6
b=2+b
#a
#b
Добавлено через 2 минуты 27 секунд
Здесь в 3-й строке должна либо выдаваться ошибка, либо печататься все значения

Код
a=10.6
b=2
#a b b b
#b
Добавлено через 1 минуту 54 секунды
Аналогично долдна быть синтаксическая ошибка (ну или любое другое сообщение об ошибке)
Код
b=2 2
Или, судя по последним двум примерам, перенос строки игнорируется и здесь фактически два оператора "b=2" и пустышка "2"?

Добавлено через 3 минуты 58 секунд
Неправильно учитывается приоритет операций. Следующий пример демонстрирует это. При вычислении b поставлены скобки (которые по большому счёту не нужны, только для показывания в каком порядке должны идти вычисления). Оба выражения должны дать один и тот же результат

Код
a=2+4|5*6
b=(2+4)|(5*6)
#a
#b
Код
32
30
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
30.06.2009, 05:11  [ТС]     Пишем свой интерпретатор языка BASIC #19
Пытаюсь избавиться от этой проблемы
C++
1
a = 10.0 | 11
И вот тут у меня вопрос.При посимвольном разборе данной конкретной строки текущий символ ch в функции get_token никогда не принимает значение '.' (точка).
1)Почему?
2) Как её поймать? Я уже успел поискать про машинную запись чисел с плавающей точкой,я так понимаю,этот ньюанс требует пересмотра посимвольного разбора с помощью putback? Где-то там по дороге эта точка и теряется между цифрами...
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
30.06.2009, 12:23     Пишем свой интерпретатор языка BASIC
Еще ссылки по теме:

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

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

Или воспользуйтесь поиском по форуму:
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16824 / 5245 / 319
Регистрация: 30.03.2009
Сообщений: 14,121
Записей в блоге: 26
30.06.2009, 12:23     Пишем свой интерпретатор языка BASIC #20
Сообщение было отмечено автором темы, экспертом или модератором как ответ
Точка не должна отлавливаться. У тебя "10.0" должно идти как единая грамматическая единица (token). Собственно, потому я и предложил навести порядок, что логически у тебя некорректно написано.

Сейчас у тебя get_token выдирает число по одной циферке. Это не есть правильно. get_token за раз должен выдрать целиком грамматическую единицу. Т.е. если записано "123", то за раз будет выдрано "123", если "123.45", то "123.45". А вот если "123.45.67", то первая лексическая единица будет "123.45", а следующая "." (или ".67", если понимать дополнительный вариант записи плавающих чисел). Если это ключевое слово "print", то и будет "print" (с чем у тебя на текущий момент проблемы). Сейчас у меня под рукой нет формальных описаний, но если желаешь - ознакомлю тебя с формальными описаниями грамматики и лексики

Добавлено через 1 минуту 40 секунд
> Я уже успел поискать про машинную запись чисел с плавающей точкой,я так
> понимаю,этот ньюанс требует пересмотра посимвольного разбора с помощью
> putback?

Нет. Как я уже писал, get_token за раз должен выдрать "123.45", котору затем стандартными функциями ты превратишь в плавающее число. Возможно, я пока объясняю слишком непонятно, но если ты морально готов к перелопачиванию своей программы, могу начать пояснять более подробно.

Добавлено через 5 минут 0 секунд
Хотя нашёл в инете пример формального описания грамматики. Так что если надо - могу вкратце пояснить суть работы грамматического анализатора

Добавлено через 2 часа 51 минуту 6 секунд
================================================================

В общем, появилось немного свободного времени на работе. Так что родил примерно следующее пояснение

Есть две вещи разного уровня: грамматика и лексика. Грамматика - это по сути дела правила построения слов из отдельных букв. Лексика - построение предложений из слов (а последние построены по правилам грамматики)

Давай рассмотрим простейший вариант того, какие грамматические единицы (token'ы) должны поддерживаться нашим интерпретатором:
  • константы (числа): целочисленные и плавающие
  • идентификаторы (имена переменных)
  • ключевые слова: в нашем случае пока только PRINT, но для простоты в будущем развитии я бы ввёл ещё LET
  • знаки операций, которые принято называть разделителями (delimiter) : + - * / | & ~ =
  • признак конца строки (поскольку конец строки будет являться разделителем между предложениями)
  • признак конца файла

Задача парсера, который по сути дела является грамматическим анализатором, является нарезка входного текста на слова (token'ы). При этом грамматический анализатор будет пропускать комментарии, ненужные пробелы и знаки табуляции. Мы будем считать, что один вызов GetToken (я всё-таки обзову именно так, чтобы не путать с тем, что сейчас есть у тебя) вынимает из входного потока одно слово (token). Как это будет представлено на уровне данных, пока не рассматриваем (чисто чтобы теорию понять)

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

Код
let a = 5.123 # комментарий
print a
последовательные вызовы GetToken должны вернуть следующий набор значений:
  • Ключевое слово (keyword) LET
  • Идентификатор (identifier) A
  • Операция (delimiter) =
  • Плавающая константа (float constant) 5.123
  • EOL (конец строки)
  • Ключевое слово PRINT
  • Идентификатор A
  • EOL (конец строки)
  • EOF (конец файла)

При этом комментарии за пределы парсера вообще не вылезают. Дабы остальным компонентам с ними не возиться

Теперь, как это всё должно выглядеть технически. По результату вызова GetToken фактически должен возвращать в качестве результата некие два значения. Первым значением является непосредственное строковое представление слова, которое полезно для печати диагностики, отладки, а так же необходимо для разбора идентификаторов и констант. Т.е. для нашего примера этими строковыми значениями будут "LET", "A", "=", "5.123" и т.д. Вторым значением является значение некоего enum'а, которое удобно обрабатывать в виде целочисленного значения и которое является описанием того, что у нас записано в строке. Таким образом пара этих значений полностью описывает наш token

Как конкретно сделать enum - зависит от того, как тебе удобно работать.
Я бы сделал так:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
enum parser_TokenType
{
  TOKEN_NULL = 0,
 
  TOKEN_CONST_INT,   // целочисленная константа
  TOKEN_CONST_FLOAT, // плавающая константа
 
  TOKEN_IDENT,       // идентификатор
 
  TOKEN_KW_LET,      // ключевое слово LET
  TOKEN_KW_PRINT,    // ключевое слово PRINT
 
  TOKEN_DELIM_EQUAL, // знак "="
  TOKEN_DELIM_PLUS,  // знак "+"
  TOKEN_DELIM_MINUS, // знак "-"
  ....
 
  TOKEN_EOL,         // конец строки
  TOKEN_EOF,         // конец файла
 
  TOKEN_LAST
};
При этом для значений TOKEN_CONST_INT, TOKEN_CONST_FLOAT и TOKEN_IDENT нам необходимо второе значение (которое описывает строковое представление token'а), а в остальных случаях элемент enum'а полностью описывает наш token

В итоге интерфейс нашего грамматического анализатора будет примерно таким:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* Инициализация парсера. В качестве file_name подаём имя файла,
 * с которым работаем. В случае проблем с открытием файла возвращаем
 * false, если всё в порядке - true */
extern bool parser_Init (const char *file_name);
 
/* Процедура parser_GetToken вынимает очередное слово из нашего входного
 * потока. Порезультату работы записываются переменные parser_CurToken
 * и parser_LastTokenStr. Процедура parser_GetToken возвращает то же значение,
 * что записывается в parser_CurToken (для удобства работы).
 * В перменные parser_CurFile, parser_CurLine записывается информация
 * о положении в файле текущего token'а (для выдачи ошибок) */
extern parser_TokenType parser_GetToken (void);
extern parser_TokenType parser_CurToken;
extern char *parser_CurTokenStr;
extern char *parser_CurFile;
extern unsigned parser_CurLine;
 
/* Отладочная печать текущего token'а */
extern void parser_PrintCurToken (void);
 
/* Завершение работы */
extern void parser_Finish (void);
Попробуй наваять парсер (который по сути является грамматическим анализатором) примерно по такому интерфейсу. Либо меняй его на своё усмотрение, лишь бы остался принцип того, что на один вызов GetToken вытаскивается грамматическая единица целиком. И заодно встрой в программу под макросом или опцией отладочную печать из-под своего парсера. Тут тоже можешь экспериментировать. Печать может быть интегрирована прямо вовнутрь GetToken'а, можно её вызывать снаружи. Лишь бы тебе было удобно по печатям отслеживать процесс работы

Ну и весь грамматический анализатор полезно выделить в отдельный файл

Добавлено через 14 минут 34 секунды
Вот примерное формальное описание грамматики:

Код
Const = ConstInt | ConstFloat
ConstInt = Digit { Digit }
ConstFloat = Digit { Digit } "." Digit { Digit }
Ident = Letter { Letter | Digit }
Letter = "A" | "B" | ... | "y" | "z"
Digit = "0" | "1" | ... | "9"
KeywordLet = "LET"
KeywordPRINT = "PRINT"
То, что написано с заглавной буквы, представляет собой правило. То, что в кавычках - непосредственно указанные внутри кавычек символ(ы). Символ | означает один из вариантов, то, что заключено в фигурные скобки - это ноль или более потворений того, что заключено в них (скобках)

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

Самая первая задача - научиться нарезать на слова в случае, когда нет грамматических ошибок. А уже потом пытаться отсекать ошибочные случаи
Yandex
Объявления
30.06.2009, 12:23     Пишем свой интерпретатор языка BASIC
Закрытая тема Создать тему
Опции темы

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