diff --git a/ПЛАН.txt b/ПЛАН.txt index be384e1..4cc8dfb 100644 --- a/ПЛАН.txt +++ b/ПЛАН.txt @@ -28,12 +28,14 @@ - [x] Инициализация локальных переменных - [x] Локальные массивы - [x] Локальные константы -- [ ] Циклы «для» +- [x] Циклы «для» +- [ ] Объявление переменных в цикле «для» +- [ ] Перечисления в каком-нибудь виде +- [ ] Switch-case-ы в каком-то виде +- [ ] Динамическая память в каком-нибудь виде - [ ] Типизированные константы - [ ] Через интерпретацию ПП - [ ] Передача переменных по ссылке -- [ ] Перечисления в каком-нибудь виде -- [ ] Динамическая память в каком-нибудь виде - [ ] Знаковые целые - [ ] Что дальше? diff --git a/исходники/компилятор.rs b/исходники/компилятор.rs index 1112a56..fcdbca1 100644 --- a/исходники/компилятор.rs +++ b/исходники/компилятор.rs @@ -304,19 +304,10 @@ fn скомпилировать_выражение(пп: &mut ПП, имена: return Ok(Тип::Цел64); } if let Some((переменная, локация)) = найти_переменную_по_имени(&лексема.текст, имена, текущая_процедура, локальные_имена) { - match локация { - Локация::Глобальная => { - пп.заплатки_неиниц_указателей.push(пп.код.len()); - пп.код.push(Инструкция{ вид: ВидИнструкции::ГлобальныеДанные(переменная.смещение), лок: лексема.лок.clone() }); - } - Локация::Стековая => { - пп.код.push(Инструкция{ вид: ВидИнструкции::Кадр(переменная.смещение), лок: лексема.лок.clone() }); - } - } + локация.протолкнуть_смещение(пп, &лексема.лок, переменная.смещение); if переменная.тип.примитивный() { прочитать_примитивный_тип(&лексема.лок, &mut пп.код, переменная.тип.размер(&имена.структуры))?; } - return Ok(переменная.тип.clone()); } @@ -566,15 +557,7 @@ fn скомпилировать_выражение(пп: &mut ПП, имена: } // СДЕЛАТЬ: принимать срез по аналогии с ПечатьСтроки - match локация { - Локация::Глобальная => { - пп.заплатки_неиниц_указателей.push(пп.код.len()); - пп.код.push(Инструкция{ вид: ВидИнструкции::ГлобальныеДанные(переменная.смещение), лок: имя.лок.clone() }); - } - Локация::Стековая => { - пп.код.push(Инструкция{ вид: ВидИнструкции::Кадр(переменная.смещение), лок: имя.лок.clone() }); - } - } + локация.протолкнуть_смещение(пп, &имя.лок, переменная.смещение); пп.код.push(Инструкция{ вид: ВидИнструкции::Целое(*размер), лок: имя.лок.clone() }); пп.код.push(Инструкция{ вид: ВидИнструкции::Ввод, лок: имя.лок.clone() }); return Ok(Тип::Цел64); @@ -622,16 +605,7 @@ fn скомпилировать_выражение(пп: &mut ПП, имена: let тип_размера_среза = скомпилировать_выражение(пп, имена, текущая_процедура, локальные_имена, &размер_среза, временный_размер)?; проверить_типы(&размер_среза.лок(), &Тип::Цел64, &тип_размера_среза)?; - - match локация { - Локация::Глобальная => { - пп.заплатки_неиниц_указателей.push(пп.код.len()); - пп.код.push(Инструкция{ вид: ВидИнструкции::ГлобальныеДанные(переменная.смещение), лок: имя.лок.clone() }); - } - Локация::Стековая => { - пп.код.push(Инструкция{ вид: ВидИнструкции::Кадр(переменная.смещение), лок: имя.лок.clone() }); - } - } + локация.протолкнуть_смещение(пп, &имя.лок, переменная.смещение); let тип_начала = скомпилировать_выражение(пп, имена, текущая_процедура, локальные_имена, &начало_среза, временный_размер)?; проверить_типы(&размер_среза.лок(), &Тип::Цел64, &тип_начала)?; @@ -747,15 +721,7 @@ fn скомпилировать_выражение(пп: &mut ПП, имена: return Err(()); }; - match локация { - Локация::Глобальная => { - пп.заплатки_неиниц_указателей.push(пп.код.len()); - пп.код.push(Инструкция{вид: ВидИнструкции::ГлобальныеДанные(переменная.смещение), лок: имя.лок.clone()}); - } - Локация::Стековая => { - пп.код.push(Инструкция{вид: ВидИнструкции::Кадр(переменная.смещение), лок: имя.лок.clone()}); - } - } + локация.протолкнуть_смещение(пп, &имя.лок, переменная.смещение); // СДЕЛАТЬ: проверка входа за границы массива. @@ -880,6 +846,20 @@ enum Локация { Стековая, } +impl Локация { + fn протолкнуть_смещение(&self, пп: &mut ПП, лок: &Лок, смещение: i32) { + match self { + Локация::Глобальная => { + пп.заплатки_неиниц_указателей.push(пп.код.len()); + пп.код.push(Инструкция{ вид: ВидИнструкции::ГлобальныеДанные(смещение), лок: лок.clone() }); + } + Локация::Стековая => { + пп.код.push(Инструкция{ вид: ВидИнструкции::Кадр(смещение), лок: лок.clone() }); + } + } + } +} + fn найти_константу_по_имени<'ы>(имя: &str, имена: &'ы Имена, локальные_имена: Option<&'ы ЛокальныеИмена>) -> Option<&'ы СкомпКонстанта> { if let Some(локальные_имена) = локальные_имена { if let Some(константа) = локальные_имена.найти_константу_по_имени(имя) { @@ -936,15 +916,7 @@ fn скомпилировать_левое_выражение(пп: &mut ПП, } Выражение::Идент(имя) => { if let Some((переменная, локация)) = найти_переменную_по_имени(&имя.текст, имена, текущая_процедура, локальные_имена) { - match локация { - Локация::Глобальная => { - пп.заплатки_неиниц_указателей.push(пп.код.len()); - пп.код.push(Инструкция{ вид: ВидИнструкции::ГлобальныеДанные(переменная.смещение), лок: имя.лок.clone() }); - } - Локация::Стековая => { - пп.код.push(Инструкция{ вид: ВидИнструкции::Кадр(переменная.смещение), лок: имя.лок.clone() }); - } - } + локация.протолкнуть_смещение(пп, &имя.лок, переменная.смещение); return Ok(переменная.тип.clone()); } @@ -959,15 +931,7 @@ fn скомпилировать_левое_выражение(пп: &mut ПП, let индекс = &аргументы[0]; if let Some((переменная, локация)) = найти_переменную_по_имени(&имя.текст, имена, текущая_процедура, локальные_имена) { - match локация { - Локация::Глобальная => { - пп.заплатки_неиниц_указателей.push(пп.код.len()); - пп.код.push(Инструкция{ вид: ВидИнструкции::ГлобальныеДанные(переменная.смещение), лок: имя.лок.clone() }); - } - Локация::Стековая => { - пп.код.push(Инструкция{ вид: ВидИнструкции::Кадр(переменная.смещение), лок: имя.лок.clone() }); - } - } + локация.протолкнуть_смещение(пп, &имя.лок, переменная.смещение); let тип_элемента = индексировать_массив(&имя.лок, переменная, пп, имена, текущая_процедура, локальные_имена, индекс, временный_размер)?; return Ok(тип_элемента); } @@ -1027,6 +991,16 @@ fn скомпилировать_левое_выражение(пп: &mut ПП, } } +fn записать_тип(лок: &Лок, код: &mut Vec<Инструкция>, структуры: &HashMap, тип: &Тип) -> Результат<()> { + if тип.примитивный() { + записать_примитивный_тип(лок, код, тип.размер(структуры))?; + } else { + код.push(Инструкция{ вид: ВидИнструкции::Целое(тип.размер(структуры)), лок: лок.clone() }); + код.push(Инструкция{ вид: ВидИнструкции::СкопироватьПамять, лок: лок.clone() }); + } + Ok(()) +} + fn скомпилировать_утверждение(пп: &mut ПП, имена: &Имена, текущая_процедура: &СкомпПроцедура, локальные_имена: &mut ЛокальныеИмена, утверждение: &Утверждение, отложенные_возвраты: &mut Vec) -> Результат<()> { match утверждение { Утверждение::Присваивание{ключ, левое, правое} => { @@ -1034,12 +1008,7 @@ fn скомпилировать_утверждение(пп: &mut ПП, имен let правый_тип = скомпилировать_выражение(пп, имена, текущая_процедура, локальные_имена, правое, &mut временный_размер)?; let левый_тип = скомпилировать_левое_выражение(пп, имена, текущая_процедура, локальные_имена, левое, &mut временный_размер)?; проверить_типы(правое.лок(), &левый_тип, &правый_тип)?; - if левый_тип.примитивный() { - записать_примитивный_тип(&ключ.лок, &mut пп.код, левый_тип.размер(&имена.структуры))?; - } else { - пп.код.push(Инструкция{ вид: ВидИнструкции::Целое(левый_тип.размер(&имена.структуры)), лок: ключ.лок.clone() }); - пп.код.push(Инструкция{ вид: ВидИнструкции::СкопироватьПамять, лок: ключ.лок.clone() }); - } + записать_тип(&ключ.лок, &mut пп.код, &имена.структуры, &левый_тип)?; if временный_размер > 0 { пп.код.push(Инструкция{ вид: ВидИнструкции::ОсвободитьСоСтека(временный_размер as u64), лок: ключ.лок.clone() }) } @@ -1193,12 +1162,7 @@ fn скомпилировать_утверждение(пп: &mut ПП, имен let тип = скомпилировать_выражение(пп, имена, текущая_процедура, локальные_имена, &выражение, &mut временный_размер)?; проверить_типы(&выражение.лок(), &результат.тип, &тип)?; пп.код.push(Инструкция{ вид: ВидИнструкции::Кадр(результат.смещение_от_кадра), лок: ключ.лок.clone() }); - if тип.примитивный() { - записать_примитивный_тип(&ключ.лок, &mut пп.код, тип.размер(&имена.структуры))?; - } else { - пп.код.push(Инструкция{ вид: ВидИнструкции::Целое(тип.размер(&имена.структуры)), лок: ключ.лок.clone() }); - пп.код.push(Инструкция{ вид: ВидИнструкции::СкопироватьПамять, лок: ключ.лок.clone() }); - } + записать_тип(&ключ.лок, &mut пп.код, &имена.структуры, &тип)?; let размер_всех_локальных_переменных = локальные_имена .стек_областей .iter() @@ -1251,6 +1215,69 @@ fn скомпилировать_утверждение(пп: &mut ПП, имен пп.код[точка_условного_прыжка].вид = ВидИнструкции::УсловныйПрыжок(точка_выхода); Ok(()) } + Утверждение::Для{ключ, индекс, нижняя_граница, верхняя_граница, тело} => { + if let Some((переменная, локация)) = найти_переменную_по_имени(&индекс.текст, имена, текущая_процедура, локальные_имена) { + проверить_типы(&индекс.лок, &Тип::Цел64, &переменная.тип)?; + + // Инициализация индексной переменной + let mut временный_размер = 0; + let тип_нижней_границы = скомпилировать_выражение(пп, имена, текущая_процедура, локальные_имена, &нижняя_граница, &mut временный_размер)?; + проверить_типы(&нижняя_граница.лок(), &переменная.тип, &тип_нижней_границы)?; + локация.протолкнуть_смещение(пп, &индекс.лок, переменная.смещение); + записать_тип(&индекс.лок, &mut пп.код, &имена.структуры, &переменная.тип)?; + if временный_размер > 0 { + пп.код.push(Инструкция{ вид: ВидИнструкции::ОсвободитьСоСтека(временный_размер as u64), лок: ключ.лок.clone() }); + временный_размер = 0; + } + + // Проверка условия выхода из цикла + let точка_условия = пп.код.len(); + локация.протолкнуть_смещение(пп, &индекс.лок, переменная.смещение); + прочитать_примитивный_тип(&индекс.лок, &mut пп.код, переменная.тип.размер(&имена.структуры))?; + let тип_верхней_границы = скомпилировать_выражение(пп, имена, текущая_процедура, локальные_имена, &верхняя_граница, &mut временный_размер)?; + проверить_типы(&верхняя_граница.лок(), &переменная.тип, &тип_верхней_границы)?; + пп.код.push(Инструкция{ вид: ВидИнструкции::ЦелБольше, лок: ключ.лок.clone() }); + let точка_условного_прыжка = пп.код.len(); + пп.код.push(Инструкция{ вид: ВидИнструкции::Ноп, лок: ключ.лок.clone() }); + if временный_размер > 0 { + пп.код.push(Инструкция{ вид: ВидИнструкции::ОсвободитьСоСтека(временный_размер as u64), лок: ключ.лок.clone() }); + } + + let переменная_тип = переменная.тип.clone(); + let переменная_смещение = переменная.смещение; + + // Тело + локальные_имена.стек_областей.push(Default::default()); + for утверждение in тело.iter() { + скомпилировать_утверждение(пп, имена, текущая_процедура, локальные_имена, утверждение, отложенные_возвраты)?; + } + let размер_области = локальные_имена + .стек_областей + .pop() + .expect("Тело цикла «для» не трогает область которую мы протолкнули") + .размер_переменных_на_стеке; + if размер_области > 0 { + пп.код.push(Инструкция{ вид: ВидИнструкции::ОсвободитьСоСтека(размер_области as u64), лок: ключ.лок.clone() }); + } + + // Инкремент + локация.протолкнуть_смещение(пп, &индекс.лок, переменная_смещение); + прочитать_примитивный_тип(&индекс.лок, &mut пп.код, переменная_тип.размер(&имена.структуры))?; + пп.код.push(Инструкция{ вид: ВидИнструкции::Целое(1), лок: ключ.лок.clone() }); + пп.код.push(Инструкция{ вид: ВидИнструкции::ЦелСложение, лок: ключ.лок.clone() }); + локация.протолкнуть_смещение(пп, &индекс.лок, переменная_смещение); + записать_тип(&индекс.лок, &mut пп.код, &имена.структуры, &переменная_тип)?; + + пп.код.push(Инструкция{ вид: ВидИнструкции::Прыжок(точка_условия), лок: ключ.лок.clone() }); + let точка_выхода = пп.код.len(); + пп.код[точка_условного_прыжка].вид = ВидИнструкции::УсловныйПрыжок(точка_выхода); + + return Ok(()) + } + + диагностика!(&индекс.лок, "ОШИБКА", "Неизвестная переменная «{имя}»", имя = индекс.текст); + Err(()) + } Утверждение::ДекларацияПеременной{ключ, имя, тип, значение} => { let размер_всех_локальных_переменных = локальные_имена .стек_областей @@ -1271,12 +1298,7 @@ fn скомпилировать_утверждение(пп: &mut ПП, имен let тип_значения = скомпилировать_выражение(пп, имена, текущая_процедура, локальные_имена, значение, &mut временный_размер)?; проверить_типы(&значение.лок(), &тип_переменной, &тип_значения)?; пп.код.push(Инструкция{ вид: ВидИнструкции::Кадр(смещение), лок: ключ.лок.clone() }); - if тип_переменной.примитивный() { - записать_примитивный_тип(&ключ.лок, &mut пп.код, тип_переменной.размер(&имена.структуры))?; - } else { - пп.код.push(Инструкция{ вид: ВидИнструкции::Целое(тип_переменной.размер(&имена.структуры)), лок: ключ.лок.clone() }); - пп.код.push(Инструкция{ вид: ВидИнструкции::СкопироватьПамять, лок: ключ.лок.clone() }); - } + записать_тип(&ключ.лок, &mut пп.код, &имена.структуры, &тип_переменной)?; if временный_размер > 0 { пп.код.push(Инструкция{ вид: ВидИнструкции::ОсвободитьСоСтека(временный_размер as u64), лок: ключ.лок.clone() }); } diff --git a/исходники/лексика.rs b/исходники/лексика.rs index 4a4a0e1..48a898a 100644 --- a/исходники/лексика.rs +++ b/исходники/лексика.rs @@ -7,6 +7,7 @@ pub const ПРИСТАВКИ_ПРЕПИНАНИЙ: &[(&[char], ВидЛексе (&['('], ВидЛексемы::ОткрытаяСкобка), (&[')'], ВидЛексемы::ЗакрытаяСкобка), (&[';'], ВидЛексемы::ТочкаЗапятая), + (&['.', '.'], ВидЛексемы::ТочкаТочка), (&['.'], ВидЛексемы::Точка), (&[':', '='], ВидЛексемы::Присваивание), (&[':'], ВидЛексемы::Двоеточие), @@ -29,6 +30,7 @@ pub const КЛЮЧЕВЫЕ_СЛОВА: &[(&str, ВидЛексемы)] = &[ ("то", ВидЛексемы::КлючТо), ("иначе", ВидЛексемы::КлючИначе), ("пока", ВидЛексемы::КлючПока), + ("для", ВидЛексемы::КлючДля), ("вернуть", ВидЛексемы::КлючВернуть), ("не", ВидЛексемы::КлючНе), ("мн", ВидЛексемы::КлючМн), @@ -57,6 +59,7 @@ pub enum ВидЛексемы { КлючТо, КлючИначе, КлючПока, + КлючДля, КлючВернуть, КлючНе, КлючМн, @@ -76,6 +79,7 @@ pub enum ВидЛексемы { ЗакрытаяСкобка, ТочкаЗапятая, Точка, + ТочкаТочка, Двоеточие, Запятая, Плюс, @@ -106,6 +110,7 @@ impl ВидЛексемы { ВидЛексемы::КлючТо => Сущ{текст: "«то»", род: Род::Сред}, ВидЛексемы::КлючИначе => Сущ{текст: "«иначе»", род: Род::Сред}, ВидЛексемы::КлючПока => Сущ{текст: "«пока»", род: Род::Сред}, + ВидЛексемы::КлючДля => Сущ{текст: "«для»", род: Род::Сред}, ВидЛексемы::КлючВернуть => Сущ{текст: "«вернуть»", род: Род::Сред}, ВидЛексемы::КлючНе => Сущ{текст: "«не»", род: Род::Сред}, ВидЛексемы::КлючМн => Сущ{текст: "«мн»", род: Род::Сред}, @@ -126,6 +131,7 @@ impl ВидЛексемы { ВидЛексемы::ЗакрытаяСкобка => Сущ{текст: "закрытая скобка", род: Род::Жен}, ВидЛексемы::ТочкаЗапятая => Сущ{текст: "точка с запятой", род: Род::Жен}, ВидЛексемы::Точка => Сущ{текст: "точка", род: Род::Жен}, + ВидЛексемы::ТочкаТочка => Сущ{текст: "точка точка", род: Род::Жен}, ВидЛексемы::Двоеточие => Сущ{текст: "двоеточие", род: Род::Сред}, ВидЛексемы::Запятая => Сущ{текст: "запятая", род: Род::Жен}, ВидЛексемы::Плюс => Сущ{текст: "плюс", род: Род::Муж}, @@ -262,7 +268,9 @@ impl<'a> Лексер<'a> { while self.символы.get(self.позиция).map(|сим| сим.is_numeric()).unwrap_or(false) { self.отрезать_символ(); } - if let Some('.') = self.символы.get(self.позиция) { + // Это условие крайне важно, чтобы разобрать выражение + // "1..10" как "1", "..", "10". + if Some('.') == self.символы.get(self.позиция).cloned() && Some('.') != self.символы.get(self.позиция + 1).cloned() { self.отрезать_символ(); while self.символы.get(self.позиция).map(|сим| сим.is_numeric()).unwrap_or(false) { self.отрезать_символ(); diff --git a/исходники/синтаксис.rs b/исходники/синтаксис.rs index 987584f..6e14c22 100644 --- a/исходники/синтаксис.rs +++ b/исходники/синтаксис.rs @@ -211,6 +211,7 @@ pub enum Утверждение { Присваивание{ключ: Лексема, левое: Выражение, правое: Выражение}, Выражение{выражение: Выражение}, Пока{ключ: Лексема, условие: Выражение, тело: Vec<Утверждение>}, + Для{ключ: Лексема, индекс: Лексема, нижняя_граница: Выражение, верхняя_граница: Выражение, тело: Vec<Утверждение>}, Если{ключ: Лексема, условие: Выражение, тело: Vec<Утверждение>, иначе: Vec<Утверждение>}, Вернуть{ключ: Лексема, выражение: Option<Выражение>}, ДекларацияПеременной{ключ: Лексема, имя: Лексема, тип: Выражение, значение: Option<Выражение>}, @@ -258,6 +259,16 @@ fn разобрать_утверждение(лекс: &mut Лексер) -> Р let тело = разобрать_блок_кода(лекс)?; Ok(Утверждение::Пока{ключ, условие, тело}) } + ВидЛексемы::КлючДля => { + let ключ = лекс.вытащить_лексему().unwrap(); + let индекс = лекс.вытащить_лексему_вида(&[ВидЛексемы::Идент])?; + let _ = лекс.вытащить_лексему_вида(&[ВидЛексемы::Присваивание])?; + let нижняя_граница = Выражение::разобрать(лекс)?; + let _ = лекс.вытащить_лексему_вида(&[ВидЛексемы::ТочкаТочка])?; + let верхняя_граница = Выражение::разобрать(лекс)?; + let тело = разобрать_блок_кода(лекс)?; + Ok(Утверждение::Для{ключ, индекс, нижняя_граница, верхняя_граница, тело}) + } ВидЛексемы::КлючВернуть => { let ключ = лекс.вытащить_лексему().unwrap(); if лекс.подсмотреть_лексему()?.вид == ВидЛексемы::ТочкаЗапятая { diff --git a/примеры/02-цикл.хуя b/примеры/02-цикл.хуя index b0a27c5..f3aa8b9 100644 --- a/примеры/02-цикл.хуя +++ b/примеры/02-цикл.хуя @@ -4,8 +4,5 @@ про главная() нч пер а: цел = 0; - пока а мн РАЗМЕР нч - печать(а, «\н»); - а := а + 1; - кц + для пер а := 1..РАЗМЕР то печать(а-1, «\н»); кц diff --git a/примеры/04-физз-базз.хуя b/примеры/04-физз-базз.хуя index 23c7d14..63d0e01 100644 --- a/примеры/04-физз-базз.хуя +++ b/примеры/04-физз-базз.хуя @@ -1,17 +1,14 @@ вкл прелюдия; -конст ЛИМИТ = 100; - про главная() нч - // // СДЕЛАТЬ: цикл «для» - // для счет := 1..ЛИМИТ нч - пер счет: цел = 1; - пока счет мн ЛИМИТ + 1 нч // СДЕЛАТЬ: ввести операцию мнрв + // СДЕЛАТЬ: объявление индексной переменной прямо в цикле + // для пер счет := 1..100 нч + пер счет: цел; + для счет := 1..100 нч если счет % 15 == 0 то печать(«ФиззБазз\н»); // СДЕЛАТЬ: подумать как сократить конструкцию «иначе то если» иначе то если счет % 3 == 0 то печать(«Физз\н»); иначе то если счет % 5 == 0 то печать(«Базз\н»); иначе то печать(счет, «\н»); - счет := счет + 1; кц кц diff --git a/примеры/06-рейлиб.хуя b/примеры/06-рейлиб.хуя index e6f0d16..c7fc849 100644 --- a/примеры/06-рейлиб.хуя +++ b/примеры/06-рейлиб.хуя @@ -24,13 +24,12 @@ про главная() нч установить_случайное_семя(69); - пер индекс: цел = 0; - пока индекс мн ОБЪЁМ нч + пер индекс: цел; + для индекс := 0..ОБЪЁМ-1 нч позиции(индекс).икс := случайное_вещественное()*50.0; позиции(индекс).игрек := случайное_вещественное()*10.0; дельты(индекс).икс := случайное_вещественное()*400.0; дельты(индекс).игрек := случайное_вещественное()*200.0; - индекс := индекс + 1; кц открыть_окно(800, 600, адрес(«Привет, Raylib!»)); @@ -45,8 +44,7 @@ начать_рисовать(); очистить_фон(ЦВЕТ_ФОНА); - индекс := 0; - пока индекс мн ОБЪЁМ нч + для индекс := 0..ОБЪЁМ-1 нч если не пауза нч пер новая_позиция: Вектор2; новая_позиция.икс := позиции(индекс).икс + дельты(индекс).икс*дельта_времени; @@ -64,7 +62,6 @@ позиции(индекс).игрек как цел, ШИРИНА, ВЫСОТА, ЦВЕТ_КРАСНЫЙ); - индекс := индекс + 1; кц закончить_рисовать(); кц diff --git a/примеры/07-рекурсия.хуя b/примеры/07-рекурсия.хуя index 81b3639..35e3d35 100644 --- a/примеры/07-рекурсия.хуя +++ b/примеры/07-рекурсия.хуя @@ -5,10 +5,7 @@ про отступ(ширина: цел) нч пер индекс: цел = 0; - пока индекс мн ширина нч - печать(« »); - индекс := индекс + 1; - кц + для индекс := 1..ширина то печать(« »); кц про дерево(глубина: цел) нч