Форум программистов, компьютерный форум 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)
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
01.10.2009, 23:35  [ТС]     Пишем свой интерпретатор языка BASIC #161
В смысле негативные? Что-то не понял В 33 ревизии прогонял тесты все,всё было ок вроде .. Может,там надо в скрипте указать --noinit-errors? А также если делаешь эталоны печати,тоже надо скрипт подправить.
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16825 / 5246 / 321
Регистрация: 30.03.2009
Сообщений: 14,126
Записей в блоге: 26
01.10.2009, 23:36     Пишем свой интерпретатор языка BASIC #162
Да я просто смотрю - все тесты содержат синтаксические ошибки, а файлы *.stdout содержат выдачу интерпретатора об ошибке (ревизия 33)
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
01.10.2009, 23:42  [ТС]     Пишем свой интерпретатор языка BASIC #163
А,ну так да,мы давно же обсуждали,что тесты лучше негативные делать,чтобы отлавливать правильно ошибки и если что править диалог с пользователем.
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16825 / 5246 / 321
Регистрация: 30.03.2009
Сообщений: 14,126
Записей в блоге: 26
01.10.2009, 23:47     Пишем свой интерпретатор языка BASIC #164
Так надо иметь две группы тестов: позитивные и негативные. Позитивные должны нормально проходить интерпретацию и выдавать результат (и сравнивать его с эталоном). Вот про это я и говорил, что надо аккуратно эталонную печать править (изменился исходник теста, изменилась работа оператора PRINT, исправлена ошибка и т.п.). В первую очередь важно иметь хороший набор позитивных тестов

С негативными тестами как правило всё сложнее, но в твоём варианте вполне приемлимо.

Заметил у тебя, что ошибки исполнения выдаются с неправильной привязкой к исходнику - с той, что соответствовала последнему токену. Поэтому в операторах я так же храню привязку, чтобы при сломе его исполнения выругаться правильно
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
02.10.2009, 06:15  [ТС]     Пишем свой интерпретатор языка BASIC #165
Сделал OPTION BASE <val>, а также теперь общая форма записи для DIM такая:
PureBasic
1
DIM array1(a TO n,...) [,] [arrayN(a TO n,...)]
или
PureBasic
1
DIM array1(a,...,n) [,] [arrayN(a,...,n)]
Или можно компоновать обе.Пишу в-общем сказать,что в репах уже 35 ревизия,но вот уже какое-то время на страничке пректа информация не обновляется(там у них баг по-ходу),хотя её видно в самом репозитории ( http://basin.svn.sourceforge.net/viewvc/basin/ ) ,а также можно забрать с SVN,так вот на всякий случай говорю,если будет желание глянуть.
Пример исходника
example
PureBasic
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
    'Here we define and calculate array
   OPTION BASE -2
   DIM array(3,3,3),array2(-3 TO -1,-4 TO 3) 'dimensions (3,8)
 
   LET i = -2
   LET j = -2
   LET k = -2
   LET n = 1
 
   WHILE i < 1
     WHILE j < 1
      WHILE k < 1
     LET array(i,j,k) = n
     PRINT "array(",i,",",j,",",k,") is: ",array(i,j,k):PRINT
         LET k = k+1
         LET n = n+1
      WEND
         LET k = -2
     LET j = j+1
     WEND
     LET i = i+1
     LET j = -2
   WEND   
 
   '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
   PRINT:PRINT "The variant with (a TO n,...)":PRINT
   
   LET i = -3
   LET j = -4
   LET n = 1
   
   WHILE i < 0
      WHILE j < 4
         LET array2(i,j) = n
     PRINT "array2(",i,",",j,") is: ",array2(i,j):PRINT
         LET j = j+1
         LET n = n+1
      WEND
      LET j = -4
      LET i = i+1
   WEND
   
   PRINT 
   
   '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
   'Тут меняем base по умолчанию на 0(было -2)
   OPTION BASE 0
   'Here we define and calculate array
   DIM array3(3,10)
 
   LET array3(0,9) = 2
 
   LET i = 0
   LET j = 0
 
   WHILE i < 3
     WHILE j < 10
    LET array3(i,j) = i+j
    PRINT "array3(",i,",",j,") is: ",array3(i,j):PRINT
    LET j = j+1
     WEND
     LET i = i+1
     LET j = 0
   WEND
 
   PRINT
   
   ' Работа с многомерным массивом
   DIM AA(2,3,4)
   LET COUNT = 100
   LET i = 0
   WHILE i < 2 
     LET j = 0
     WHILE j < 3 
       LET k = 0
       WHILE k < 4 
         LET COUNT = COUNT + 1
         LET AA(i,j,k) = COUNT
         LET k = k + 1
       WEND
       LET j = j + 1
     WEND
     LET i = i + 1
   WEND
   PRINT COUNT:    PRINT     ' expected 124
   PRINT AA(1,0,1):PRINT     ' expected 114

Ну всё,пока делаю for,do и твои опции trace попробую сделать.
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16825 / 5246 / 321
Регистрация: 30.03.2009
Сообщений: 14,126
Записей в блоге: 26
02.10.2009, 09:39     Пишем свой интерпретатор языка BASIC #166
На работе попробую. Исходник тестовой программы уже внушаитъ
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16825 / 5246 / 321
Регистрация: 30.03.2009
Сообщений: 14,126
Записей в блоге: 26
02.10.2009, 12:34     Пишем свой интерпретатор языка BASIC #167
Для конструкции "DIM A (-1)" невнятная ошибка "Too large variable: A"
Для "DIM A (2 TO 1)" тоже не очень понятное "Size of massive unknown"

Но это так, чисто по мелочи
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16825 / 5246 / 321
Регистрация: 30.03.2009
Сообщений: 14,126
Записей в блоге: 26
02.10.2009, 17:24     Пишем свой интерпретатор языка BASIC #168
Тест на IF

PureBasic
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
' Тест на разные формы записи оператора IF
' Каждый тест повторяем по два раза, чтобы проверить обе ветки исполнения
 
 
 
PRINT "----- IF Cond THEN Stmt -----------------------------------------------"
 
LET A=1
LET B=-1
IF A=1 THEN LET B=100
PRINT B ' expected 100
 
LET A=2
LET B=-1
IF A=1 THEN LET B=100
PRINT B ' expected -1
 
 
 
PRINT "----- IF Cond THEN Stmt:Stmt ------------------------------------------"
 
LET A=1
LET B=-1 : LET C=-1
IF A=1 THEN LET B=100 : LET C=101
PRINT B ' expected 100
PRINT C ' expected 101
 
LET A=2
LET B=-1 : LET C=-1
IF A=1 THEN LET B=100 : LET C=101
PRINT B ' expected -1
PRINT C ' expected -1
 
 
 
PRINT "----- IF Cond THEN Stmt ELSE Stmt -------------------------------------"
 
LET A=1
IF A=1 THEN LET B=100 ELSE LET B=200
PRINT B ' expected 100
 
LET A=1
IF A=1 THEN LET B=100 ELSE LET B=200
PRINT B ' expected 200
 
 
 
PRINT "----- IF Cond THEN Stmt:Stmt ELSE Stmt:Stmt ---------------------------"
 
LET A=1
IF A=1 THEN LET B=100 : LET C=101 ELSE LET B=200 : LET C=201
PRINT B ' expected 100
PRINT C ' expected 101
 
LET A=2
IF A=1 THEN LET B=100 : LET C=101 ELSE LET B=200 : LET C=201
PRINT B ' expected 200
PRINT C ' expected 201
 
 
 
PRINT "----- IF Cond THEN ----------------------------------------------------"
PRINT "        Stmt"
PRINT "      END IF"
 
LET A=1
LET B=-1
IF A=1 THEN
  LET B=100
END IF
PRINT B ' expected 100
 
LET A=2
LET B=-1
IF A=1 THEN
  LET B=100
END IF
PRINT B ' expected -1
 
 
 
PRINT "----- IF Cond THEN ----------------------------------------------------"
PRINT "      END IF"
 
LET A=1
LET B=-1
IF A=1 THEN
END IF
PRINT B ' expected -1
 
 
 
PRINT "----- IF Cond THEN ----------------------------------------------------"
PRINT "        Stmt"
PRINT "      ELSE"
PRINT "        Stmt"
PRINT "      END IF"
 
LET A=1
IF A=1 THEN
  LET B=100
ELSE
  LET B=200
END IF
PRINT B ' expected 100
 
LET A=2
IF A=1 THEN
  LET B=100
ELSE
  LET B=200
END IF
PRINT B ' expected 200
 
 
 
PRINT "----- IF Cond THEN ----------------------------------------------------"
PRINT "        Stmt"
PRINT "      ELSE"
PRINT "      END IF"
 
LET A=1
LET B=-1
IF A=1 THEN
  LET B=100
ELSE
END IF
PRINT B ' expected 100
 
LET A=2
LET B=-1
IF A=1 THEN
  LET B=100
ELSE
END IF
PRINT B ' expected -1
 
 
 
PRINT "----- IF Cond THEN ----------------------------------------------------"
PRINT "      ELSE"
PRINT "        Stmt"
PRINT "      END IF"
 
LET A=1
LET B=-1
IF A=1 THEN
ELSE
  LET B=200
END IF
PRINT B ' expected -1
 
LET A=2
LET B=-1
IF A=1 THEN
ELSE
  LET B=200
END IF
PRINT B ' expected 200
 
 
 
PRINT "----- IF Cond THEN ----------------------------------------------------"
PRINT "      ELSE"
PRINT "      END IF"
 
LET A=1
LET B=-1
IF A=1 THEN
ELSE
END IF
PRINT B ' expected -1
 
LET A=2
LET B=-1
IF A=1 THEN
ELSE
END IF
PRINT B ' expected -1
 
 
 
' ----- îÅÇÁÔÉ×ÎÙÅ ÔÅÓÔÙ ------------------------------------------------------
 
' IF A=1
 
' IF A=1 THEN
 
' IF A=1 THEN LET A=1 ELSE
 
' IF A=1 THEN ELSE LET A=1
 
' IF A=1 THEN
'   LET A=1
 
' IF A=1 THEN
'   LET A=1
' END
 
' IF A=1 THEN
'   LET A=1 ELSE LET A=2
 
' IF A=1 THEN LET A=1
' ELSE LET A=2
 
' IF A=1 THEN LET A=1 ELSE LET A=2 END IF
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16825 / 5246 / 321
Регистрация: 30.03.2009
Сообщений: 14,126
Записей в блоге: 26
03.10.2009, 10:48     Пишем свой интерпретатор языка BASIC #169
Цитата Сообщение от #pragma Посмотреть сообщение
и твои опции trace попробую сделать.
Чтобы поддержать в том виде, в котором я предлагал (OPTION TRACE_WRITE 1 и т.д.) нужно будет эти конструкции в промежуточном представлении поддерживать. Я на это пока забил и сделал опцию командной строки, по которой фича включается на всю программу целиком (а не на фрагмент). Реализовывать буду немного по-другому, если не забуду, потом расскажу
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
04.10.2009, 07:25  [ТС]     Пишем свой интерпретатор языка BASIC #170
Столкнулся с небольшим затруднением в цикле FOR.Получается так,что мы должны знать начальное и конечное значения счётчика,а также шаг уже на момент интерпретации,чтобы правильно сгенерировать условие.Т.е получается,что выражения FOR i=A+B недопустимы,и после знака равно должна следовать константа,как максимум со знаком минус.Ведь по сути при генерации условия нужно знать значение в начале и в конце,их разницу,а также шаг.Потому что это влияет на то,каким будет условие (<= или >=).Смотрел в справку qbasic,вроде там тоже написано что обе границы должны быть <value>.Но как-то не особо расшифровано там.Мы же не можем менять весь подход к интерпретации cbranch только из-за одного цикла,а также некрасиво делать интерпретацию с подсчётом внутри синтаксического разборщика.Я не знаю,как лучше,просто решил пойти на такую уловку
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  static Value syntax_runtimePrimary()
  {
     switch (parser_GetToken())
     {
        case TOKEN_CONST_INT: {
           Value int_x(::parser_CurTokenStr, INT);
           return int_x;
        }
        case TOKEN_CONST_FLOAT: {
           Value float_x(::parser_CurTokenStr, FLOAT);
           return float_x;
        }
        case TOKEN_DELIM_MINUS: {
           return -syntax_runtimePrimary();
        }
        default:error(WRONG_EXPRESSION);
     }
     // This is bad
     //это просто чтобе не получать предупр.компилятора
     Value null_x("0",INT);
     return null_x;
  }
То есть взял функцию из старой версии просто для считывания констант,в том числе отрицательных,и использую в синтаксическом разборщике(в том числе при объявлении массивов).Если в присваивании счётчику было бы хоть даже простое выражение,это существенно затруднит генерацию условия.Не знаю,как лучше сделать,оставить как есть или построить run-time версию синт.разборщика,чтобы мог простые выражения считать на своём этапе;но с другой стороны,надо ли?

Добавлено через 1 час 47 минут
Вообще,сначала мне пришла в голову мысль просто как-то запихать нужную строку в поток считывания из исходника,предварительно сформировав её,просто немного коряво выглядит такое решение,не знаю,хороший ли это вариант..
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16825 / 5246 / 321
Регистрация: 30.03.2009
Сообщений: 14,126
Записей в блоге: 26
04.10.2009, 11:36     Пишем свой интерпретатор языка BASIC #171
Я что-то трудностей не понял. Почему там должна быть константа?

PureBasic
1
2
3
FOR A=X1 TO X2 STEP X3
  <stmt_list>
NEXT A
эквивалентно

PureBasic
1
2
3
4
5
LET A=X1
WHILE A<=X2
  <stmt_list>
  LET A=A+X3
WEND
Если язык накладывает ограничение на то, чтоб X1, X2 и X3 были константами, для парсера это всего лишь дополнительная проверка, что построенные выражения являются константами
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
04.10.2009, 11:40  [ТС]     Пишем свой интерпретатор языка BASIC #172
Цитата Сообщение от Evg Посмотреть сообщение
Я что-то трудностей не понял. Почему там должна быть константа?

PureBasic
1
2
3
FOR A=X1 TO X2 STEP X3
  <stmt_list>
NEXT A
эквивалентно

PureBasic
1
2
3
4
5
LET A=X1
WHILE A<=X2
  <stmt_list>
  LET A=A+X3
WEND
Потому что заранее неясно,
PureBasic
1
WHILE A<=X2
или
PureBasic
1
WHILE A>=X2
,также не понятно,увеличивать ли А или уменьшать
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16825 / 5246 / 321
Регистрация: 30.03.2009
Сообщений: 14,126
Записей в блоге: 26
04.10.2009, 11:44     Пишем свой интерпретатор языка BASIC #173
Проверил на qbasic выражения поддерживает везде, кроме step'а. Т.е. всё равно общий синтаксис остаётся в виде

Код
ForStatement = "FOR" Ident "=" Expr "TO" Expr ["STEP" ConstExpr]
ConstExpr = ["+"|"-"] (CONST_INT | CONST_FLOAT)
При этом ConstExpr нужно строить в виде Expr, который содержит константу. Тогда просто строишь код в виде эквивалента указанного выше цикла while

Добавлено через 1 минуту
Цитата Сообщение от #pragma Посмотреть сообщение
Потому что заранее неясно,
PureBasic
1
WHILE A<=X2
или
PureBasic
1
WHILE A>=X2
А какая разница, известно или неизвестно. Ты код построй, а на интерпретации оно либо попадёт в нужную ветку, либо не попадёт. Когда строишь код для "WHILE A1<A2" тебе ведь тоже заранее неизвестно

Цитата Сообщение от #pragma Посмотреть сообщение
также не понятно,увеличивать ли А или уменьшать
Строить операцию "плюс". Если написано "step -5", значит это выродится в "+(-5)"

Добавлено через 47 секунд
Говоря, что один код эквивалентен другому, я имею в виду, что конечное представление должно выглядеть точно так же.
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
04.10.2009, 11:51  [ТС]     Пишем свой интерпретатор языка BASIC #174
Хорошо,поставим вопрос по -другому.На основании каких данных мы строим условие вручную,которого по сути нет в FOR(а нужно сгенерировать).Мы должны знать,какой знак отношения поставить между операндами.
То есть
C++
1
rel_expr = expr_NewBinOp(operand1,operand2,OP_REL_LESS_EQUAL);
или
C++
1
rel_expr = expr_NewBinOp(operand1,operand2,OP_REL_GREAT_EQUAL);
А на основании чего мы можем поставить этот знак?
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16825 / 5246 / 321
Регистрация: 30.03.2009
Сообщений: 14,126
Записей в блоге: 26
04.10.2009, 11:53     Пишем свой интерпретатор языка BASIC #175
> А на основании чего мы можем поставить этот знак?

На основания описания приинципа работы FOR. Проблему наконец просёк. Так же и просёк, почему в STEP допустима только константа. В итоге получается так, что если в STEP - положительная константа, то знак должен быть "<=", если отрицательная, то ">="
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16825 / 5246 / 321
Регистрация: 30.03.2009
Сообщений: 14,126
Записей в блоге: 26
06.10.2009, 21:17     Пишем свой интерпретатор языка BASIC #176
Для ревизии 38

PureBasic
1
2
3
4
5
FOR I=1 TO 5
  FOR J=I+1 TO 5
    PRINT "in loop I=", I, " ,J=", J : PRINT
NEXT J : NEXT I
PRINT "after loop I=", I, " ,J=", J : PRINT
Не происходит инкрементации I

Ещё есть ELSEIF. Для порядку его тоже надо бы поддержать. Я тоже что-то позабыл
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16825 / 5246 / 321
Регистрация: 30.03.2009
Сообщений: 14,126
Записей в блоге: 26
09.10.2009, 13:54     Пишем свой интерпретатор языка BASIC #177
Тест на ELSEIF
PureBasic
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
' Тест на разные формы появления ELSEIF
' Каждый тест повторяем по нескольку раз, чтобы проверить разные ветки исполнения
 
PRINT "----- IF Cond THEN ----------------------------------------------------"
PRINT "        Stmt"
PRINT "      ELSE IF Cond THEN"
PRINT "      END IF"
 
LET A=1
LET B=-1
IF A=1 THEN
  LET B=100
ELSEIF A=2 THEN
END IF
PRINT B ' expected 100
 
LET A=2
LET B=-1
IF A=1 THEN
  LET B=100
ELSEIF A=2 THEN
END IF
PRINT B ' expected -1
 
 
 
PRINT "----- IF Cond THEN ----------------------------------------------------"
PRINT "        Stmt"
PRINT "      ELSE IF Cond THEN"
PRINT "        Stmt"
PRINT "      END IF"
 
LET A=1
LET B=-1
IF A=1 THEN
  LET B=100
ELSEIF A=2 THEN
  LET B=200
END IF
PRINT B ' expected 100
 
LET A=2
LET B=-1
IF A=1 THEN
  LET B=100
ELSEIF A=2 THEN
  LET B=200
END IF
PRINT B ' expected 200
 
LET A=3
LET B=-1
IF A=1 THEN
  LET B=100
ELSEIF A=2 THEN
  LET B=200
END IF
PRINT B ' expected -1
 
 
 
PRINT "----- IF Cond THEN ----------------------------------------------------"
PRINT "        Stmt"
PRINT "      ELSE IF Cond THEN"
PRINT "        Stmt"
PRINT "      ELSE"
PRINT "      END IF"
 
LET A=1
LET B=-1
IF A=1 THEN
  LET B=100
ELSEIF A=2 THEN
  LET B=200
ELSE
END IF
PRINT B ' expected 100
 
LET A=2
LET B=-1
IF A=1 THEN
  LET B=100
ELSEIF A=2 THEN
  LET B=200
ELSE
END IF
PRINT B ' expected 200
 
LET A=3
LET B=-1
IF A=1 THEN
  LET B=100
ELSEIF A=2 THEN
  LET B=200
ELSE
END IF
PRINT B ' expected -1
 
 
 
PRINT "----- IF Cond THEN ----------------------------------------------------"
PRINT "        Stmt"
PRINT "      ELSE IF Cond THEN"
PRINT "        Stmt"
PRINT "      ELSE"
PRINT "        Stmt"
PRINT "      END IF"
 
LET A=1
LET B=-1
IF A=1 THEN
  LET B=100
ELSEIF A=2 THEN
  LET B=200
ELSE
  LET B=300
END IF
PRINT B ' expected 100
 
LET A=2
LET B=-1
IF A=1 THEN
  LET B=100
ELSEIF A=2 THEN
  LET B=200
ELSE
  LET B=300
END IF
PRINT B ' expected 200
 
LET A=3
LET B=-1
IF A=1 THEN
  LET B=100
ELSEIF A=2 THEN
  LET B=200
ELSE
  LET B=300
END IF
PRINT B ' expected 300
 
 
 
PRINT "----- IF Cond THEN ----------------------------------------------------"
PRINT "        Stmt"
PRINT "      ELSE IF Cond THEN"
PRINT "        Stmt"
PRINT "      ELSE IF Cond THEN"
PRINT "        Stmt"
PRINT "      ELSE"
PRINT "        Stmt"
PRINT "      END IF"
 
LET A=1
LET B=-1
IF A=1 THEN
  LET B=100
ELSEIF A=2 THEN
  LET B=200
ELSEIF A=3 THEN
  LET B=300
ELSE
  LET B=400
END IF
PRINT B ' expected 100
 
LET A=2
LET B=-1
IF A=1 THEN
  LET B=100
ELSEIF A=2 THEN
  LET B=200
ELSEIF A=3 THEN
  LET B=300
ELSE
  LET B=400
END IF
PRINT B ' expected 200
 
LET A=3
LET B=-1
IF A=1 THEN
  LET B=100
ELSEIF A=2 THEN
  LET B=200
ELSEIF A=3 THEN
  LET B=300
ELSE
  LET B=400
END IF
PRINT B ' expected 300
 
LET A=4
LET B=-1
IF A=1 THEN
  LET B=100
ELSEIF A=2 THEN
  LET B=200
ELSEIF A=3 THEN
  LET B=300
ELSE
  LET B=400
END IF
PRINT B ' expected 400
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
09.10.2009, 14:39  [ТС]     Пишем свой интерпретатор языка BASIC #178
Попробовал пройти процесс создания Makefile'а с помощью GNU Autotools.Оно в этом проекте по ходу не надо,да?Я так понял,это для каких-то больших проектов.Или может всё-таки Makefile'ы могут быть разными на разных платформах?
Насчёт ELSEIF я немного не догоняю,разве это не будет увеличивать стек? Ведь нужно позаботиться,чтобы ELSEIF принадлежал определённому IF.
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16825 / 5246 / 321
Регистрация: 30.03.2009
Сообщений: 14,126
Записей в блоге: 26
09.10.2009, 15:59     Пишем свой интерпретатор языка BASIC #179
Цитата Сообщение от #pragma Посмотреть сообщение
Попробовал пройти процесс создания Makefile'а с помощью GNU Autotools.Оно в этом проекте по ходу не надо,да?Я так понял,это для каких-то больших проектов.Или может всё-таки Makefile'ы могут быть разными на разных платформах?
Я так и ни разу этой штуковиной не пользовался, т.к. если и приходилось работать на разных архитектурах, то их было маленькое количество (две, три, четыре), но никак не 100. Мы как-то без автоконфигов и автомэйков обходились, так что я особо и не знаю, как с ними работать

Цитата Сообщение от #pragma Посмотреть сообщение
Насчёт ELSEIF я немного не догоняю,разве это не будет увеличивать стек? Ведь нужно позаботиться,чтобы ELSEIF принадлежал определённому IF.
А что за стек?
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
09.10.2009, 16:24     Пишем свой интерпретатор языка BASIC
Еще ссылки по теме:

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

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

Или воспользуйтесь поиском по форуму:
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
09.10.2009, 16:24  [ТС]     Пишем свой интерпретатор языка BASIC #180
Я имею ввиду что нельзя выйти из функции syntax_stmtNewCBranch до того,как создастся полная ветка IF ... END IF,этих ELSEIF ведь может быть очень много,... в смысле сделать цикл и добавлять ветки пока не увидим END IF,так что-ли?
Yandex
Объявления
09.10.2009, 16:24     Пишем свой интерпретатор языка BASIC
Закрытая тема Создать тему
Опции темы

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