Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.69/1283: Рейтинг темы: голосов - 1283, средняя оценка - 4.69
Временно недоступен
 Аватар для #pragma
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926

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

20.06.2009, 20:03. Показов 253440. Ответов 464
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Благодаря форуму и Evg в частности интерпретатор развивается, потихоньку превращаясь в простенький интерпретатор QBASIC.
Некоторые из самых старых версий сохранились в теме и ссылки на них будут добавлены в это сообщение,а также ссылки на другие темы,связанные с этой.

Репозиторий с проектом находится тут, там же есть возможность в браузере посмотреть историю ревизий (английский в логах весьма примитивен,комментарии и рекомендации можете писать в личку),а также скачать самый последний архив репозитория в формате .tar.gz
Если кто-то пользуется Subversion,скачать исходники можно так:
Code
1
svn co https://basin.svn.sourceforge.net/svnroot/basin basin
Эти темы возникли в результате моих вопросов по ходу написания:
Технический приём для формирования согласованных данных
https://www.cyberforum.ru/c-linux/thread46096.html
Вопрос по svn (Subversion)
Создание системы тестирования ПО.
Вопрос про разные реализации бэйсиков
Можно ли выразить порядковый номер элемента массива через индексы?
[C++] Какие флаги указать линкеру для компиляции программы?
Как можно определить переменную в файле configure.in,чтобы её можно было использовать в Makefile?
Странный SIGSEGV, или что зависит от порядка написания интерфейса класса
https://www.cyberforum.ru/c-linux/thread61324.html
Альтернативная версия интерпретатора от 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;
31
Лучшие ответы (1)
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
20.06.2009, 20:03
Ответы с готовыми решениями:

Пишем свой интерпретатор языка BASIC
Добрый день. Я смотрю, тут на форуме была тема коллективного написания интерпретатора BASIC на языке С++. Я очень извиняюсь, но я...

Пишем свой strlen
Всем привет, вырвал часть задание из общего задание по написанию своего string. На данном этапе столкнулся с проблемой (хотел написать...

Пишем свой чекер
Я хочу написать свой чекер, но не знаю с чего начать? Кто знает основные принцип работы чекеров прошу объясните.

464
Эксперт С++
 Аватар для niXman
3211 / 1459 / 74
Регистрация: 09.08.2009
Сообщений: 3,441
Записей в блоге: 2
25.11.2011, 17:56
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от taras atavin Посмотреть сообщение
Ну слаг же предлагают, а цитаты с вики я дал.
или ты прикидываешь, или второе.
1
Эксперт С++
 Аватар для Nameless One
5828 / 3479 / 358
Регистрация: 08.02.2010
Сообщений: 7,448
25.11.2011, 17:57
Цитата Сообщение от taras atavin Посмотреть сообщение
Почему нельзя получение каждого следующего предложения начинать просто по факту окончания предыдущего? Зачем нужен именно стартовый символ?
чтобы иметь именно входную точку парсера. Ты же не будешь (возвращаясь к моему примеру) начинать синтаксический анализ твоей программы с парсинга списка параметров функции
0
 Аватар для taras atavin
4226 / 1796 / 211
Регистрация: 24.11.2009
Сообщений: 27,562
25.11.2011, 17:58
Цитата Сообщение от Nameless One Посмотреть сообщение
если непонятно, представь, что у тебя каждый нетерминал грамматики представлен в виде функции компилятора/интерпретатора, которая принимает исходный код (или, если у тебя есть лексер, поток лексем) и возвращает, к примеру, синтаксическое дерево. Стартовому символу (стартовому нетерминалу) будет соответствовать некоторая функция. Так вот, если передать этой функции исходный код программы (или поток лексем), то она (если код корректен синтаксически) вернет тебе синтаксическое дерево, которое и будет представлять тебе программу
Эйси. А символ то какую роль выполняет? Почему нельзя эту функцию подразумевать без всякого символа?
0
Эксперт С++
 Аватар для niXman
3211 / 1459 / 74
Регистрация: 09.08.2009
Сообщений: 3,441
Записей в блоге: 2
25.11.2011, 17:58
Nameless One, то, о чем ты говоришь, называется контекстом парсера. я об этом несколькими постами ранее сказал. но ТС походу в танке.
0
 Аватар для taras atavin
4226 / 1796 / 211
Регистрация: 24.11.2009
Сообщений: 27,562
25.11.2011, 18:01
Цитата Сообщение от Nameless One Посмотреть сообщение
Ты же не будешь (возвращаясь к моему примеру) начинать синтаксический анализ твоей программы с парсинга списка параметров функции
А как я на него с бухты барахты попаду? Предыдущее предложение ведь не посреди текущего закончилось.

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

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

Добавлено через 8 минут
А пример чего нибудь контекстно-зависимого для сравнения с арифметическим выражением?
0
Эксперт С++
5058 / 3118 / 271
Регистрация: 11.11.2009
Сообщений: 7,044
25.11.2011, 21:00
Цитата Сообщение от taras atavin Посмотреть сообщение
То есть это первое предложение?
Да, это своего рода базис, к нему в конечном итоге должно "свернуться" корректное предложение грамматики.
0
 Аватар для taras atavin
4226 / 1796 / 211
Регистрация: 24.11.2009
Сообщений: 27,562
26.11.2011, 07:39
Я кажется понял, о чём речь. О слове
Pascal
1
program
. Первое предложение должно начинаться с начала файла.
0
Эксперт С++
5058 / 3118 / 271
Регистрация: 11.11.2009
Сообщений: 7,044
26.11.2011, 13:36
taras atavin, первое предложение языка, задающегося данной грамматикой, а не statement - да, должно начинаться с начала файла, разумеется. Но к целевому символу это имеет мало отношения. Суть целевого символа в том, что анализатор в конечном итоге сможет свернуть корректную программу на языке, задаваемом грамматикой, к целевому символу proram. Потому он и называется целевым, что цель парсера - прийти к нему и только к нему одному в результате свёрток по правилам грамматики.

Добавлено через 1 час 21 минуту
Возьмём грамматику, предложенную Nameless One. Я дополню её, чтобы она была законченной:
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
program -> definitions
definitions -> definitions definition | 'EPS'
definition -> prototype body
prototype -> typespec qualifier '(' paramlist ')'
paramlist -> param | paramlist ',' param | 'EPS'
param -> typespec qualifier
body -> '{' statements '}'
statements -> statement ';' | statements statement ';' | 'EPS'
typespec -> 'int' # Пусть в качестве типа выступает int и только он (для простоты)
statement -> 'return 0' # Предложением будет мифический терминал 'return 0' (также для простоты)
qualifier -> letters # Я немного изменил грамматику для простоты, теперь как funid, так и varid являются qualifier, который, в свою очередь - набор строчных букв латиницы (как минимум одна буква)
letters -> letter letters | letter 'EPS'
letter -> 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z'
Также для наглядности я позволил себе внести левую рекурсивность в грамматику. При построении парсера от неё надо избавляться (для этого существует простой алгоритм).

Рассмотрим теперь простую программу на данном языке, состоящую из одной функции:
Code
1
2
3
4
int a(int b)
{
    return 0;
}
Для начала опишу упрощённую схему работы связки лексический анализатор-синтаксический анализатор. Лексический анализатор возвращает синтаксическому лексемы. Синтаксический (обычно реализуемый при помощи конечного автомата со стековой памятью, хотя есть и другий способы) заталкивает эту лексему в стек (т.н. сдвиг). В стеке хранятся как терминалы, так и нетерминалы (как они там появляются - далее). Если в стеке оказался набор символов (терминальных или нетерминальных), соответствующий какому-то правилу грамматики, эти символы удаляются из стека, а их место занимает нетерминал, порождающий удалённый набор символов. Так в стеке (на начальном этапе содержащем только лексемы) появляются нетерминалы.
А теперь заделаемся парсером и выполним его действия (примерно, лишь для пояснения):
Изначально стек пуст. Лексический анализатор вернул синтаксическому лексему 'int'. Произошёл сдвиг. В стеке
Code
1
'int'
Для данного терминала существует порождающее правило typespec -> 'int'. Поэтому происходит свёртка по этому правилу. В стеке
Code
1
typespec
Далее вернулась лексема a'. В стеке
Code
1
typespec 'a'
Терминал 'a' сворачивается к нетерминалу letter. Происходит свёртка. В стеке
Code
1
typespec letter
letter, в свою очередь, сворачивается к letters по правилу letters -> letter 'EPS'. Ну а letters сворачивается к qualifier (я объединил две свёртки для наглядности). В стеке
Code
1
typespec qualifier
Возвращается '('. В стеке
Code
1
typespec qualifier '('
Затем возвращается 'int', который можно свернуть к typespec. В стеке
Code
1
typespec qualifier '(' typespec
Возвращается 'b', который снова проводим через цепочку letter -> letters -> qualifier. В стеке
Code
1
typespec qualifier '(' typespec qualifier
Последовательность typespec qualifier можно свернуть к param, а его сразу можно свернуть к paramlist. В стеке
Code
1
typespec qualifier '(' paramlist
Возвращается ')' В стеке
Code
1
typespec qualifier '(' paramlist ')'
Данный набор символов можно свернуть к prototype, что и делает парсер. В стеке
Code
1
prototype
Возвращается '{', 'return 0', ';', '}'. 'return 0' и ';' можно свернуть к statement, а его с statements. Последовательность '{', statements, '}' сворачивается к body. В стеке
Code
1
prototype body
Это можно свернуть к definition, а его, в свою очередь, к definitions. В стеке
Code
1
definitions
Программа завершилась, поэтому definitions можно свернуть к program, к целевому символу. Целевой символ получается только если программа полностью разобрана и она корректна (т.е. удовлетворяет языку, определяемому грамматикой, по которой строился парсер).
Будь в программе две функции, после разбора первой в стеке оказалось бы
Code
1
definitions
, после разбора второй
Code
1
definitions definition
Который также свернулся бы к definitions, а только этот definitions в итоге свернулся бы к program. Т.е. целевой символ получатся только единожды, в конце разбора. Если парсер не смог дойти до него, или же после того, как он его получил, лексер продолжает возвращать лексемы, то это свидетельствует об ошибке.
4
 Аватар для taras atavin
4226 / 1796 / 211
Регистрация: 24.11.2009
Сообщений: 27,562
26.11.2011, 13:58
silent_1991, Эйси. А какое всё это имеет отношение к стартовому символу?
0
Каратель
Эксперт С++
6610 / 4029 / 401
Регистрация: 26.03.2010
Сообщений: 9,273
Записей в блоге: 1
26.11.2011, 14:20
Цитата Сообщение от silent_1991 Посмотреть сообщение
к нему в конечном итоге должно "свернуться" корректное предложение грамматики
Цитата Сообщение от silent_1991 Посмотреть сообщение
Суть целевого символа в том, что анализатор в конечном итоге сможет свернуть корректную программу на языке, задаваемом грамматикой, к целевому символу proram.
сворачивать надо к аксиоме, а не к целевому символу
0
 Аватар для taras atavin
4226 / 1796 / 211
Регистрация: 24.11.2009
Сообщений: 27,562
26.11.2011, 14:28
С целевым понятно. Про стартовый растолкуйте.
1
Эксперт С++
5058 / 3118 / 271
Регистрация: 11.11.2009
Сообщений: 7,044
26.11.2011, 14:39
Jupiter, хм, а разве это не синонимы?
0
 Аватар для taras atavin
4226 / 1796 / 211
Регистрация: 24.11.2009
Сообщений: 27,562
26.11.2011, 14:46
Нда. В такой терминологии не то что еврей, чёрт ногу сломит.
0
Эксперт С++
5058 / 3118 / 271
Регистрация: 11.11.2009
Сообщений: 7,044
26.11.2011, 14:49
Насколько мне известно, аксиома грамматики, стартовый символ грамматики, целевой символ грамматики, начальный символ грамматики ("символ" также можно заменить на "нетерминал") - всё это одно и то же понятие - базис разбора, своего рода.

Добавлено через 2 минуты
taras atavin, похоже, вы не поняли, в каком контексте здесь понимается термин "символ". В данном случае символ - это не символ в общепринятом определении. Символом может быть терминал ('int', 'do', 'template'), или нетерминал (program, statement, typespec). Вот один такой нетерминальный (не конечный, промежуточный) символ является целевым (начальным, стартовым и т.д.), т .е. таким, к которому всё должно прийти в конце концов.
2
 Аватар для taras atavin
4226 / 1796 / 211
Регистрация: 24.11.2009
Сообщений: 27,562
26.11.2011, 14:57
int входит в алфавит?
0
Каратель
Эксперт С++
6610 / 4029 / 401
Регистрация: 26.03.2010
Сообщений: 9,273
Записей в блоге: 1
26.11.2011, 15:15
Цитата Сообщение от taras atavin Посмотреть сообщение
int входит в алфавит?
алфавит - это множество символов(именно символов char), тобишь все буквы a-zA-Z, все цифры, и все символы допустимые в языке

int - это терминал => int - цепочка символы которой входят в алфавит
1
 Аватар для Kastaneda
5232 / 3205 / 362
Регистрация: 12.12.2009
Сообщений: 8,143
Записей в блоге: 2
05.01.2013, 20:37
Пару вопросов - оно собирается под виндой? и что нужно сделать, чтоб коммитить? Всмысле зарегистрироваться на sourceforge или может меня кто-то должен в проект добавить?
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
05.01.2013, 20:37
Помогаю со студенческими работами здесь

пишем свой троян с нуля
Всем привет)))соглашусь, что изобретаю велосипед, но хочется сделать все своими ручками не прибегая к open source и т.п. для повышения...

Пишем свой класс, спецификатор доступа protected
Всем привет! Из книги Р. Лафоре относительно спецификатора доступа protected: Далее пишется следующее: Возникает вопросы:...

Интерпретатор небольшого языка программирования на С++
Здравствуйте, уважаемые форумчане! Я тут где-то год назад прочитал тему Evg и #pragma о создании интерпретатора, меня эта тема очень...

Не удается откомпилировать интерпретатор М-языка
Задача: взять интерпретатор М-языка на сайте http://cmcmsu.no-ip.info/2course/model.lang.parser.sample.htm и переработать его, добавив в...

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


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

Или воспользуйтесь поиском по форуму:
460
Закрытая тема Создать тему
Новые блоги и статьи
Использование SDL3-callbacks вместо функции main() на Android, Desktop и WebAssembly
8Observer8 24.01.2026
Если вы откроете примеры для начинающих на официальном репозитории SDL3 в папке: examples, то вы увидите, что все примеры используют следующие четыре обязательные функции, а привычная функция main(). . .
моя боль
iceja 24.01.2026
Выложила интерполяцию кубическими сплайнами www. iceja. net REST сервисы временно не работают, только через Web. Написала за 56 рабочих часов этот сайт с нуля. При помощи perplexity. ai PRO , при. . .
Модель сукцессии микоризы
anaschu 24.01.2026
Решили писать научную статью с неким РОманом
http://iceja.net/ математические сервисы
iceja 20.01.2026
Обновила свой сайт http:/ / iceja. net/ , приделала Fast Fourier Transform экстраполяцию сигналов. Однако предсказывает далеко не каждый сигнал (см ограничения http:/ / iceja. net/ fourier/ docs ). Также. . .
http://iceja.net/ сервер решения полиномов
iceja 18.01.2026
Выкатила http:/ / iceja. net/ сервер решения полиномов (находит действительные корни полиномов методом Штурма). На сайте документация по API, но скажу прямо VPS слабенький и 200 000 полиномов. . .
Расчёт переходных процессов в цепи постоянного тока
igorrr37 16.01.2026
/ * Дана цепь(не выше 3-го порядка) постоянного тока с элементами R, L, C, k(ключ), U, E, J. Программа находит переходные токи и напряжения на элементах схемы классическим методом(1 и 2 з-ны. . .
Восстановить юзерскрипты Greasemonkey из бэкапа браузера
damix 15.01.2026
Если восстановить из бэкапа профиль Firefox после переустановки винды, то список юзерскриптов в Greasemonkey будет пустым. Но восстановить их можно так. Для этого понадобится консольная утилита. . .
Сукцессия микоризы: основная теория в виде двух уравнений.
anaschu 11.01.2026
https:/ / rutube. ru/ video/ 7a537f578d808e67a3c6fd818a44a5c4/
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru