# Zkouška C++ 29. 1. 2026

Autorem zadání je Petr Škoda. Čas měl být asi 3.5 hodin, ale prodlužovalo se o asi 25 minut. Studenti se shodují, že to bylo složité, obzvláště vzhledem k času. K dispozici byl custom AI chat, který ale cenzuroval delší kusy kódu a tak třetinu času nefungoval.

---

Cílem je implementovat procesor úryvků (snippetů). Program přijímá dva vstupní soubory – jeden s daty a druhý s úryvkem. Program následně dosadí hodnoty z datového souboru do souboru úryvku a vypíše výsledný text na standardní výstup.

Například uvažujme následující datový soubor,

```
person.name=Max
person.age=20
person.vegetarian=1
```

a jednoduchý soubor úryvku

```
Name: {{ person.name }}
Age: {{ person.age }}
Food preferences: {{ #if person.vegetarian }} vegetarian {{ /if }}
```

Jak je vidět, v souboru úryvku se hodně opakuje stejný prefix; můžeme jej tedy zjednodušit.

```
{{ #with person }}Name: {{ name }}
Age: {{ age }}
Food preferences: {{ #if vegetarian }} vegetarian {{ /if }}{{ /with }}
```

Šablona obsahuje tzv. řídicí značky např. `{{ person.name }}` nebo `{{ #with person }}`. Program musí:

1. načíst datový soubor do strukturované reprezentace,
2. načíst a validovat soubor úryvku,
3. vypsat soubor úryvku s vyhodnocenými řídicími značkami.

Následující sekce podrobně vysvětlují veškerou požadovanou funkcionalitu.

## Rozhraní příkazové řádky

Program očekává dva argumenty příkazové řádky (bez názvu programu). Pokud počet argumentů není přesně dva, program vypíše `Invalid number of arguments` a skončí s návratovým kódem 1.

První argument je cesta k datovému souboru. Druhý argument je cesta k souboru úryvku. Jakékoliv selhání při otevírání některého ze souborů pro čtení způsobí, že program vypíše `Can not read input files` a skončí s návratovým kódem 1.

Pokud je datový soubor neplatný, program vypíše `Invalid data file` a skončí s návratovým kódem 1. Datový soubor musí být validován ještě před otevřením souboru úryvku.

Pokud je soubor úryvku neplatný, program vypíše `Invalid snippet file` a skončí s návratovým kódem 1. Částečný výstup není povolen pro neplatné soubory úryvku. Jinými slovy, program musí soubor nejprve kompletně validovat a teprve poté ji zpracovat.

Chybová hlášení musí být vypsána na `std::cout`.

## Datový soubor

Datový soubor je řádkově orientovaný a celý se vejde do operační paměti. Řádky začínající znakem `;` mají být ignorovány, stejně jako prázdné řádky. Ostatní řádky musí obsahovat `<key>` a `<value>` oddělené znakem `=`, kde `<key>` je case-sensitive. Pokud datový soubor obsahuje více hodnot pro stejný klíč, je soubor neplatný. Pokud soubor obsahuje neplatný klíč, je neplatný celý soubor.

Jednoduchý datový soubor:

```ini
name=Alistar
age=30
```

`<key>` může obsahovat jeden nebo více podklíčů oddělených tečkou `.`. Podklíč je neprázdný řetězec a skládá se ze znaků [A–Za–z0–9], můžete použít funkci `std::isalnum`. `<value>` je vše za znakem `=` až do konce řádku, bez znaku konce řádku (`\n`). `<value>` může být prázdný řetězec.

Příklad složitějšího datového souboru:

```ini
; Alistar
student.006.name=Alistar
student.007.grades.NPRG030.grade=A
; Max
student.007.name=Max
student.007.grades.NPRG041.grade=C
; The value for the next line is an empty string.
teacher.606.name=
; The next line contains a value with white spaces.
teacher.700.name=Dr. Lada
```

Příklad neplatného datového souboru:

```ini
; Space before `=`.
name =Igor
; Invalid character in the key.
invalidCharacterInKey_=12
; Key can not end with '.'.
trainingDor.=12
; Key can not be empty.
=12
; Sub-key can not be empty.
doubleDot..=12
; Space in key is not allowed
this. isNotValid=12
```

## Soubor úryvku a řídicí značky

Řídicí značka je otevřena pomocí `{{` a uzavřena pomocí `}}`. Veškerý text mimo řídicí značky je zapsán na standardní výstup. Escapování řídicích značek není podporováno, tzn. není možné vypsat `{{` nebo `}}`.

Program musí podporovat následující řídicí značky:

- `{{<relative-key>}}`
- `{{#with <relative-key>}}`, `{{/with}}` - párová značka
- `{{#if <relative-key>}}`, `{{/if}}` - párová značka
- `{{#foreach <relative-key>}}`, `{{/foreach}}` - párová značka

Další informace ke každé řídicí značce jsou uvedeny v následujících sekcích. Značky mohou být vnořené. `<relative-key>` musí splňovat stejná kritéria jako `<key>` z části o datovém souboru.

Znaky bílého místa ( , `\t`, `\r` a `\n`) za `{{` a před `}}` je třeba ignorovat. Vícenásobné mezery po `#if`, `#with` a `#foreach` jsou také ignorovány.

Následující zápisy jsou ekvivalentní:

```
{{name}}
{{ name}}
{{ name }}
{{
  name
}}
```

Následující zápisy jsou ekvivalentní:

```
{{#if name}}
{{  #if name }}
{{
  #if
  name
}}
```

Párové řídicí značky umožňují vnoření, například:

```
{{ #with person }} {{ #if render }} {{ /if }} {{ /with }}
```

Počet úrovní vnoření není omezen. Můžete předpokládat, že soubor úryvku je neplatný, pokud:

- existují nespárované `{{` nebo `}}`,
- je použita neznámá řídicí značka,
- párové řídicí značky jsou špatně spárované nebo nesprávně vnořené.

Příklad neplatné šablony se špatně vnořenými párovými značkami:

```
{{ #with person }} {{ #if render }} {{ /with }} {{ /if }}
```

Můžete předpokládat, že se každá dvojice párových značek a její obsah vejde do hlavní paměti. Celý soubor nebo dokonce jeden řádek se však do paměti vejít nemusí.

## Odkazování na datový soubor

Řídicí značky mohou odkazovat na položku datového souboru (dvojici klíč, hodnota). Aby bylo možné získat klíč s klíčem = `<key>`, musí být `<relative-key>` zkombinován s `<base-key>`.

`<base-key>` je na začátku programu prázdný a může být měněn pomocí řídicích značek (viz následující sekce).

Odvození hodnoty `<key>`

- `<key>` = `<relative-key>` pokud je `<base-key>` prázdný
- `<key>` = `<base-key>.<relative-key>`

## Řídicí značky

Tato sekce popisuje všechny řídicí značky, které musí program podporovat. Pro každou z nich se hodnota `<key>` počítá podle pravidel uvedených v části Odkazování na datový soubor.

### Výpis hodnoty

`{{ <relative-key> }}`

Značka se vyhodnotí na hodnotu položky z datového souboru pod klíčem `<key>`. Pokud taková položka neexistuje, značka se vyhodnotí jako prázdný řetězec.

### Změna <base-key>

`{{ #with <relative-key> }}...{{ /with }}`

Tato značka změní `<base-key>` na `<key>` (získaný z `<relative-key>`) pro vnořený obsah (`...`). Změna `<base-key>` platí pouze v rámci vnořeného obsahu.

Například následující řádky produkují stejný výstup:

```
{{ person.name }}
{{ #with person }}{{ name }}{{ /with }}
```

### Podmíněné vyhodnocení

`{{ #if <relative-key> }}...{{ /if }}`

Vnořený obsah je zpracován pouze tehdy, pokud:

- existuje položka v datovém souboru s klíčem `<key>` (odvozená z `<relative-key>`),
- a hodnota této položky není prázdná.

Například pro následující datový soubor

```ini
person.good=1
person.evil=
```

a úryvek

```
-{{ #if person.good }}good{{ /if }}
-{{ #if person.neutral }}neutral{{ /if }}
-{{ #if person.evil }}evil{{ /if }}
```

je výstup

```
-good
-
-
```

### Smyčky

`{{ #foreach <relative-key> }}...{{ /foreach }}`

Tato značka slouží k iterování přes podklíče. Uvažme následující datový soubor

```ini
person.b.name=1
person.a.name=0
person.c.name=2
```

a úryvek

```
{{ #foreach person }}{{ name }},{{ /foreach }}
```

výstupem bude

```
0,1,2,
```

Značka je vyhodnocena pro každý podklíč `S` klíče `<key>` (získaného z `<relative-key>`) a to v abecedním pořadí. Pokud `<key>` nemá žádné podklíče, značka nevygeneruje žádný výstup.

Pro každý podklíč `S` se nastaví `<base-key>` pro vnořený obsah (`...`) na `<key>.S`. Změna `<base-key>` platí pouze v rámci vnořeného obsahu.

Ve výše uvedeném příkladě je tedy `<base-key>` nastaven nejprve na `person.a`, pak `person.b` a nakonec na `person.c`. Vnořený obsah `{{ name }},` je tak vyhodnocen celkem třikrát.

## Příklad - Test 17

Datový soubor:

```ini
student.0.name=Max
student.0.cpp.points=20
student.0.cpp.test=30
student.0.cpp.grade=
;
student.1.name=Ailish
student.1.cpp.points=20
student.1.cpp.test=40
student.1.cpp.grade=A
;
student.2.name=Jorg
student.2.cpp.points=15
;
student.3.name=Chuck
```

Soubor úryvku:

```
Student, Tutorials, Test, Grade, Passed
{{ #foreach student }}{{ name }},{{ #with cpp}} {{ points }}, {{ test }}, {{ grade }},{{ #if grade }} passed{{ /if}}{{ /with }}
{{ /foreach }}
```

Očekávaný výstup:

```
Student, Tutorials, Test, Grade, Passed
Max, 20, 30, ,
Ailish, 20, 40, A, passed
Jorg, 15, , ,
Chuck, , , ,
```