Skip to content

Commit

Permalink
Create lark dynamically (hedyorg#2728)
Browse files Browse the repository at this point in the history
Co-authored-by: Felienne <[email protected]>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored May 31, 2022
1 parent 267e7a2 commit 47facec
Show file tree
Hide file tree
Showing 23 changed files with 625 additions and 372 deletions.
3 changes: 3 additions & 0 deletions build-tools/heroku/on-deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ cd $scriptdir
# all CSS classes that aren't used in our application (determined by searching
# for CSS classes in the HTML templates and JavaScript files).

echo '-----> Creating lark grammar files'
python ../../content/yaml_to_lark_utils.py

echo '-----> Doing a Tailwind build'
tailwind/generate-css

Expand Down
20 changes: 10 additions & 10 deletions content/keywords/en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,15 @@ input: input
or: or
while: while
length: length
1: 1
2: 2
3: 3
4: 4
5: 5
6: 6
7: 7
8: 8
9: 9
0: 0
1: '1'
2: '2'
3: '3'
4: '4'
5: '5'
6: '6'
7: '7'
8: '8'
9: '9'
0: '0'
comma: ','
quote: "'"
147 changes: 61 additions & 86 deletions content/yaml_to_lark_utils.py
Original file line number Diff line number Diff line change
@@ -1,94 +1,69 @@
from pathlib import Path
import collections
import copy
import os
import yaml

def extract_Lark_grammar_from_yaml(only_new_lang=True):
"""Creates a lark file in ../grammars/ for all yaml files located in ../content/keywords/.
If a keyword is not yet translated, it will use the English translation of the keyword
Args:
only_new_lang (bool, optional): Specifies if only a lark file should be created for a new keyword language or for all languages. Defaults to True.
"""
input_path = '../content/keywords/'
current_grammar_path = '../grammars/'
output_path = '../grammars-transformed/'
Path(output_path).mkdir(parents=True, exist_ok=True)

yaml_languages = [f.replace('.yaml', '') for f in os.listdir(input_path) if
os.path.isfile(os.path.join(input_path, f)) and f.endswith('.yaml')]

lark_languages = [f.replace('keywords-', '').replace('.lark', '') for f in os.listdir(current_grammar_path) if
os.path.isfile(os.path.join(current_grammar_path, f)) and f.startswith('keywords')]

new_languages = [l for l in yaml_languages if not l in lark_languages]

for yaml_lang in new_languages:
yaml_filesname_with_path = os.path.join(input_path, yaml_lang + '.yaml')
default_yaml_with_path = os.path.join(input_path, 'en' + '.yaml')

with open(default_yaml_with_path, 'r', encoding='utf-8') as stream:
en_command_combinations = yaml.safe_load(stream)

with open(yaml_filesname_with_path, 'r', encoding='utf-8') as stream:
command_combinations = yaml.safe_load(stream)

lark_filesname_with_path = os.path.join(output_path, 'keywords-' + yaml_lang + '.lark')

with open(lark_filesname_with_path, 'w+', encoding='utf-8') as f:
list_of_translations = []

for command, translation in command_combinations.items():
en_translation = en_command_combinations[command]

if translation == '':
translation = en_translation

if yaml_lang != 'en':
if translation in list_of_translations:
print(f'Warning! {translation} is a duplicate translation. This is not desired when creating lark files')
else:
list_of_translations.append(translation)

if type(command) != int:
# numbers should be skipped (fh apr. 22 TODO: TBH I am not sure why they are even in the yaml?)

lowercase_commands = ['random', 'left', 'right', 'black', 'blue', 'brown', 'gray', 'green', 'orange', 'pink', 'purple', 'red', 'white', 'yellow']
# random, left and right and colors are tokens and need to be printed as they are
# other rules need to be UPPERCASE and start with _
if command not in lowercase_commands:
command_upper = command.upper()
command = '_' + command_upper

# something to add to all lines to modify rules
ending = "_SPACE?"
if translation != en_translation:
f.write(f'{command}: ("{translation}" | "{en_translation}") {ending}\n')
else:
f.write(f'{command}: "{translation}" {ending}\n')

# extract_Lark_grammar_from_yaml(True)

def relax_keywords_ar():
input_path = '../content/keywords/'
current_grammar_path = '../grammars/'
output_path = '../grammars-transformed/'
Path(output_path).mkdir(parents=True, exist_ok=True)

ar_yaml_path = os.path.join(input_path, 'ar' + '.yaml')
with open(ar_yaml_path, 'r', encoding='utf-8') as stream:
ar_yaml = yaml.safe_load(stream)
ar_yaml_relaxed = {}
for k, v in ar_yaml.items():
tatweel_appended = [f'"{l}"' for l in v]
ar_yaml_relaxed[k] = ' "ـ"* ' + ' "ـ"* '.join(tatweel_appended) + ' "ـ"* '

ar_yaml_output_path = os.path.join(output_path, 'ar' + '.yaml')

with open(ar_yaml_output_path, 'w+', encoding='utf-8') as f:
yaml.safe_dump(ar_yaml_relaxed, f, allow_unicode=True)

relax_keywords_ar()
def extract_Lark_grammar_from_yaml():
"""Creates a lark file in ../grammars/ for all yaml files located in ../content/keywords/.
If a keyword is not yet translated, it will use the English translation of the keyword
Args:
only_new_lang (bool, optional): Specifies if only a lark file should be created for a new keyword language or for all languages. Defaults to True.
"""
dirname = os.path.dirname(__file__)
input_path = os.path.join(dirname, 'keywords')
current_grammar_path = os.path.join(dirname, '../grammars')

yaml_languages = [f.replace('.yaml', '') for f in os.listdir(input_path) if
os.path.isfile(os.path.join(input_path, f)) and f.endswith('.yaml')]

template_lark = os.path.join(current_grammar_path, 'keywords-template.lark')
with open(template_lark, 'r', encoding='utf-8') as f:
template = f.read()

for yaml_lang in yaml_languages:
yaml_filesname_with_path = os.path.join(input_path, yaml_lang + '.yaml')
default_yaml_with_path = os.path.join(input_path, 'en' + '.yaml')

with open(default_yaml_with_path, 'r', encoding='utf-8') as stream:
en_command_combinations = yaml.safe_load(stream)

with open(yaml_filesname_with_path, 'r', encoding='utf-8') as stream:
command_combinations = yaml.safe_load(stream)

# We don't want to create the grammar if there are no translations
if en_command_combinations == command_combinations and yaml_lang != "en":
continue

# Create an empty dictionary -> fill with english keywords and then overwrite all translated keywords
translations = collections.defaultdict(lambda: 'Unknown Exception')
translations.update(en_command_combinations)
translations.update(command_combinations)

translation_copy = copy.deepcopy(translations)
for k, v in translation_copy.items():
if yaml_lang == "ar":
mixed_tatweel_in = ''.join([' "ـ"* ' + '"'+l+'"' for l in v]) + ' "ـ"* '
translations[k] = mixed_tatweel_in
else:
# other languages need their translations surrounded by "'s
translations[k] = '"' + v + '"'

# we use | if we have multiple options, such as repete and repète
if "|" in v:
valid_translation = ""
options = v.split("|")
valid_translation = ' | '.join(['"' + option + '"' for option in options])
translations[k] = valid_translation

translated_template = template.format(**translations)

lark_filesname_with_path = os.path.join(current_grammar_path, 'keywords-' + yaml_lang + '.lark')

with open(lark_filesname_with_path, 'w', encoding='utf-8') as f:
f.write(translated_template)

extract_Lark_grammar_from_yaml()


54 changes: 54 additions & 0 deletions grammars-transformed/ar.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
print: ' "ـ"* "ق" "ـ"* "و" "ـ"* "ل" "ـ"* '
ask: ' "ـ"* "ا" "ـ"* "س" "ـ"* "أ" "ـ"* "ل" "ـ"* '
echo: ' "ـ"* "ر" "ـ"* "د" "ـ"* "د" "ـ"* '
forward: ' "ـ"* "ت" "ـ"* "ق" "ـ"* "د" "ـ"* "م" "ـ"* '
is: ' "ـ"* "ه" "ـ"* "و" "ـ"* "|" "ـ"* "ه" "ـ"* "ي" "ـ"* '
at: ' "ـ"* "ب" "ـ"* "ش" "ـ"* "ك" "ـ"* "ل" "ـ"* '
random: ' "ـ"* "ع" "ـ"* "ش" "ـ"* "و" "ـ"* "ا" "ـ"* "ئ" "ـ"* "ي" "ـ"* '
in: ' "ـ"* "ف" "ـ"* "ي" "ـ"* '
if: ' "ـ"* "ا" "ـ"* "ذ" "ـ"* "ا" "ـ"* '
else: ' "ـ"* "و" "ـ"* "إ" "ـ"* "ل" "ـ"* "ا" "ـ"* '
1: ' "ـ"* "1" "ـ"* '
2: ' "ـ"* "2" "ـ"* '
3: ' "ـ"* "3" "ـ"* '
4: ' "ـ"* "4" "ـ"* '
5: ' "ـ"* "5" "ـ"* '
6: ' "ـ"* "6" "ـ"* '
7: ' "ـ"* "7" "ـ"* '
8: ' "ـ"* "8" "ـ"* '
9: ' "ـ"* "9" "ـ"* '
0: ' "ـ"* "0" "ـ"* '
comma: ' "ـ"* "،" "ـ"* '
quote: ' "ـ"* "''" "ـ"* '
turn: ' "ـ"* "ا" "ـ"* "س" "ـ"* "ت" "ـ"* "د" "ـ"* "ر" "ـ"* '
elif: ' "ـ"* "و" "ـ"* "إ" "ـ"* "ل" "ـ"* "ا" "ـ"* " " "ـ"* "ا" "ـ"* "ذ" "ـ"* "ا" "ـ"* '
input: ' "ـ"* "ا" "ـ"* "د" "ـ"* "خ" "ـ"* "ل" "ـ"* '
or: ' "ـ"* "أ" "ـ"* "و" "ـ"* '
while: ' "ـ"* "ب" "ـ"* "ي" "ـ"* "ن" "ـ"* "م" "ـ"* "ا" "ـ"* '
length: ' "ـ"* "ط" "ـ"* "و" "ـ"* "ل" "ـ"* '
sleep: ' "ـ"* "s" "ـ"* "l" "ـ"* "e" "ـ"* "e" "ـ"* "p" "ـ"* '
to_list: ' "ـ"* "ا" "ـ"* "ل" "ـ"* "ى" "ـ"* '
remove: ' "ـ"* "ا" "ـ"* "ز" "ـ"* "ل" "ـ"* '
add: ' "ـ"* "ا" "ـ"* "ض" "ـ"* "ف" "ـ"* '
from: ' "ـ"* "م" "ـ"* "ن" "ـ"* '
and: ' "ـ"* "و" "ـ"* '
repeat: ' "ـ"* "ك" "ـ"* "ر" "ـ"* "ر" "ـ"* '
times: ' "ـ"* "م" "ـ"* "ر" "ـ"* "ة" "ـ"* '
for: ' "ـ"* "ل" "ـ"* "ك" "ـ"* "ل" "ـ"* '
range: ' "ـ"* "ن" "ـ"* "ط" "ـ"* "ا" "ـ"* "ق" "ـ"* '
to: ' "ـ"* "ا" "ـ"* "ل" "ـ"* "ى" "ـ"* '
step: ' "ـ"* "خ" "ـ"* "ط" "ـ"* "و" "ـ"* "ة" "ـ"* '
right: ' "ـ"* "ي" "ـ"* "م" "ـ"* "ي" "ـ"* "ن" "ـ"* '
left: ' "ـ"* "ي" "ـ"* "س" "ـ"* "ا" "ـ"* "ر" "ـ"* '
purple: ' "ـ"* "ب" "ـ"* "ن" "ـ"* "ف" "ـ"* "س" "ـ"* "ج" "ـ"* "ي" "ـ"* '
red: ' "ـ"* "ا" "ـ"* "ح" "ـ"* "م" "ـ"* "ر" "ـ"* '
white: ' "ـ"* "ا" "ـ"* "ب" "ـ"* "ي" "ـ"* "ض" "ـ"* '
yellow: ' "ـ"* "ا" "ـ"* "ص" "ـ"* "ف" "ـ"* "ر" "ـ"* '
color: ' "ـ"* "ل" "ـ"* "و" "ـ"* "ن" "ـ"* '
black: ' "ـ"* "ا" "ـ"* "س" "ـ"* "و" "ـ"* "د" "ـ"* '
blue: ' "ـ"* "ا" "ـ"* "ز" "ـ"* "ر" "ـ"* "ق" "ـ"* '
brown: ' "ـ"* "ب" "ـ"* "ن" "ـ"* "ي" "ـ"* '
gray: ' "ـ"* "ر" "ـ"* "م" "ـ"* "ا" "ـ"* "د" "ـ"* "ي" "ـ"* '
green: ' "ـ"* "ا" "ـ"* "خ" "ـ"* "ض" "ـ"* "ر" "ـ"* '
orange: ' "ـ"* "ب" "ـ"* "ر" "ـ"* "ت" "ـ"* "ق" "ـ"* "ا" "ـ"* "ل" "ـ"* "ي" "ـ"* '
pink: ' "ـ"* "ز" "ـ"* "ه" "ـ"* "ر" "ـ"* "ي" "ـ"* '
98 changes: 42 additions & 56 deletions grammars/keywords-ar.lark
Original file line number Diff line number Diff line change
@@ -1,56 +1,42 @@
_PRINT: ( "ـ"* "ق" "ـ"* "و" "ـ"* "ل" "ـ"* | "print") _SPACE?
_ASK: ("اسأل" | "ask") _SPACE?
_ECHO: ("ردد" | "echo") _SPACE?
_FORWARD: ("تقدم" | "forward") _SPACE?
left: ("يسار" | "left") _SPACE?
right: ("يمين" | "right") _SPACE?
_COLOR : ("لون" | "color") _SPACE?
black: ("اسود" | "black") _SPACE?
blue: ("ازرق" | "blue") _SPACE?
brown: ("بني" | "brown") _SPACE?
gray: ("رمادي" | "gray") _SPACE?
green: ("اخضر" | "green") _SPACE?
orange: ("برتقالي" | "orange") _SPACE?
pink: ("زهري" | "pink") _SPACE?
purple: ("بنفسجي" | "purple") _SPACE?
red: ("احمر" | "red") _SPACE?
white: ("ابيض" | "white") _SPACE?
yellow: ("اصفر" | "yellow") _SPACE?
_IS: _SPACE ( "هو" | "هي" | "is") _SPACE
_AT: _SPACE ("بشكل" | "at") _SPACE
random: ("عشوائي" | "random") _SPACE?
_IN: _SPACE ("في" | "in") _SPACE
_IF: ("اذا" | "if") _SPACE
_ELSE: "وإلا" | "else"
_TURN: ("استدر" | "turn") _SPACE?
_SLEEP: ("slaap" | "sleep") _SPACE? //placing Dutch words here so it will be easier to copy-paste arabic in
_ADD_LIST: ("اضف" | "add") _SPACE
_TO_LIST: _SPACE ("الى" | "to") _SPACE
_REMOVE: ("ازل" | "remove") _SPACE
_FROM: _SPACE ("من" | "from") _SPACE

//level 5
_REPEAT: ("كرر" | "repeat") _SPACE
_TIMES: _SPACE ("مرة" | "times")

//level 8
_FOR: ("لكل" | "for") _SPACE
_RANGE: ("نطاق" | "range") _SPACE?
_TO: _SPACE ("الى" | "to") _SPACE
_STEP: ("خطوة" | "step")

//level 9
_ELIF: _SPACE? ("وإلا اذا" | "elif") _SPACE

//level 11
_INPUT: ("ادخل" | "input")

//level 14
_OR: _SPACE ("أو" | "or") _SPACE
_AND: _SPACE ("و" | "and") _SPACE

//level 17
_WHILE: ("بينما" | "while") _SPACE

//level 19
_LENGTH: ("طول" | "length")
_PRINT: ( "ـ"* "ق" "ـ"* "و" "ـ"* "ل" "ـ"* | "print") _SPACE?
_ASK: ( "ـ"* "ا" "ـ"* "س" "ـ"* "أ" "ـ"* "ل" "ـ"* | "ask") _SPACE?
_ECHO: ( "ـ"* "ر" "ـ"* "د" "ـ"* "د" "ـ"* | "echo") _SPACE?
_FORWARD: ( "ـ"* "ت" "ـ"* "ق" "ـ"* "د" "ـ"* "م" "ـ"* | "forward") _SPACE?
_TURN: ( "ـ"* "ا" "ـ"* "س" "ـ"* "ت" "ـ"* "د" "ـ"* "ر" "ـ"* | "turn") _SPACE?
left: ( "ـ"* "ي" "ـ"* "س" "ـ"* "ا" "ـ"* "ر" "ـ"* | "left") _SPACE?
right: ( "ـ"* "ي" "ـ"* "م" "ـ"* "ي" "ـ"* "ن" "ـ"* | "right") _SPACE?
black: ( "ـ"* "ا" "ـ"* "س" "ـ"* "و" "ـ"* "د" "ـ"* | "black") _SPACE?
blue: ( "ـ"* "ا" "ـ"* "ز" "ـ"* "ر" "ـ"* "ق" "ـ"* | "blue") _SPACE?
brown: ( "ـ"* "ب" "ـ"* "ن" "ـ"* "ي" "ـ"* | "brown") _SPACE?
gray: ( "ـ"* "ر" "ـ"* "م" "ـ"* "ا" "ـ"* "د" "ـ"* "ي" "ـ"* | "gray") _SPACE?
green: ( "ـ"* "ا" "ـ"* "خ" "ـ"* "ض" "ـ"* "ر" "ـ"* | "green") _SPACE?
orange: ( "ـ"* "ب" "ـ"* "ر" "ـ"* "ت" "ـ"* "ق" "ـ"* "ا" "ـ"* "ل" "ـ"* "ي" "ـ"* | "orange") _SPACE?
pink: ( "ـ"* "ز" "ـ"* "ه" "ـ"* "ر" "ـ"* "ي" "ـ"* | "pink") _SPACE?
purple: ( "ـ"* "ب" "ـ"* "ن" "ـ"* "ف" "ـ"* "س" "ـ"* "ج" "ـ"* "ي" "ـ"* | "purple") _SPACE?
red: ( "ـ"* "ا" "ـ"* "ح" "ـ"* "م" "ـ"* "ر" "ـ"* | "red") _SPACE?
white: ( "ـ"* "ا" "ـ"* "ب" "ـ"* "ي" "ـ"* "ض" "ـ"* | "white") _SPACE?
yellow: ( "ـ"* "ا" "ـ"* "ص" "ـ"* "ف" "ـ"* "ر" "ـ"* | "yellow") _SPACE?
_IS: _SPACE ("هو" | "هي" | "is") _SPACE
_SLEEP: ( "ـ"* "s" "ـ"* "l" "ـ"* "e" "ـ"* "e" "ـ"* "p" "ـ"* | "sleep") _SPACE?
_ADD_LIST: ( "ـ"* "ا" "ـ"* "ض" "ـ"* "ف" "ـ"* | "add") _SPACE
_TO_LIST: _SPACE ( "ـ"* "ا" "ـ"* "ل" "ـ"* "ى" "ـ"* | "to") _SPACE
_REMOVE: ( "ـ"* "ا" "ـ"* "ز" "ـ"* "ل" "ـ"* | "remove") _SPACE
_FROM: _SPACE ( "ـ"* "م" "ـ"* "ن" "ـ"* | "from") _SPACE
_AT: _SPACE ( "ـ"* "ب" "ـ"* "ش" "ـ"* "ك" "ـ"* "ل" "ـ"* | "at") _SPACE
random: ( "ـ"* "ع" "ـ"* "ش" "ـ"* "و" "ـ"* "ا" "ـ"* "ئ" "ـ"* "ي" "ـ"* | "random") _SPACE?
_IN: _SPACE ( "ـ"* "ف" "ـ"* "ي" "ـ"* | "in") _SPACE
_IF: ( "ـ"* "ا" "ـ"* "ذ" "ـ"* "ا" "ـ"* | "if") _SPACE
_ELSE: "ـ"* "و" "ـ"* "إ" "ـ"* "ل" "ـ"* "ا" "ـ"* | "else"
_AND: _SPACE ( "ـ"* "و" "ـ"* | "and") _SPACE
_REPEAT: ( "ـ"* "ك" "ـ"* "ر" "ـ"* "ر" "ـ"* | "repeat") _SPACE
_TIMES: _SPACE ( "ـ"* "م" "ـ"* "ر" "ـ"* "ة" "ـ"* | "times")
_FOR: ( "ـ"* "ل" "ـ"* "ك" "ـ"* "ل" "ـ"* | "for") _SPACE
_RANGE: ( "ـ"* "ن" "ـ"* "ط" "ـ"* "ا" "ـ"* "ق" "ـ"* | "range") _SPACE?
_TO: _SPACE ( "ـ"* "ا" "ـ"* "ل" "ـ"* "ى" "ـ"* | "to") _SPACE
_STEP: "ـ"* "خ" "ـ"* "ط" "ـ"* "و" "ـ"* "ة" "ـ"* | "step"
_ELIF: _SPACE? ( "ـ"* "و" "ـ"* "إ" "ـ"* "ل" "ـ"* "ا" "ـ"* " " "ـ"* "ا" "ـ"* "ذ" "ـ"* "ا" "ـ"* | "elif") _SPACE
_INPUT: ( "ـ"* "ا" "ـ"* "د" "ـ"* "خ" "ـ"* "ل" "ـ"* | "input")
_OR: _SPACE ( "ـ"* "أ" "ـ"* "و" "ـ"* | "or") _SPACE
_WHILE: ( "ـ"* "ب" "ـ"* "ي" "ـ"* "ن" "ـ"* "م" "ـ"* "ا" "ـ"* | "while") _SPACE
_LENGTH: "ـ"* "ط" "ـ"* "و" "ـ"* "ل" "ـ"* | "length"
_COLOR : ( "ـ"* "ل" "ـ"* "و" "ـ"* "ن" "ـ"* | "color") _SPACE?
42 changes: 42 additions & 0 deletions grammars/keywords-bg.lark
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
_PRINT: ("принтирай" | "print") _SPACE?
_ASK: ("попитай" | "ask") _SPACE?
_ECHO: ("покажи" | "echo") _SPACE?
_FORWARD: ("напред" | "forward") _SPACE?
_TURN: ("завий" | "turn") _SPACE?
left: ("ляво" | "left") _SPACE?
right: ("дясно" | "right") _SPACE?
black: ("черно" | "black") _SPACE?
blue: ("синьо" | "blue") _SPACE?
brown: ("кафяво" | "brown") _SPACE?
gray: ("сиво" | "gray") _SPACE?
green: ("зелено" | "green") _SPACE?
orange: ("оранжево" | "orange") _SPACE?
pink: ("розово" | "pink") _SPACE?
purple: ("лилаво" | "purple") _SPACE?
red: ("червено" | "red") _SPACE?
white: ("бяло" | "white") _SPACE?
yellow: ("жълто" | "yellow") _SPACE?
_IS: _SPACE ("е" | "is") _SPACE
_SLEEP: ("спи" | "sleep") _SPACE?
_ADD_LIST: ("добави" | "add") _SPACE
_TO_LIST: _SPACE ("до" | "to") _SPACE
_REMOVE: ("премахни" | "remove") _SPACE
_FROM: _SPACE ("от" | "from") _SPACE
_AT: _SPACE ("в" | "at") _SPACE
random: ("произволно" | "random") _SPACE?
_IN: _SPACE ("в" | "in") _SPACE
_IF: ("ако" | "if") _SPACE
_ELSE: "иначе" | "else"
_AND: _SPACE ("и" | "and") _SPACE
_REPEAT: ("повтори" | "repeat") _SPACE
_TIMES: _SPACE ("пъти" | "times")
_FOR: ("за" | "for") _SPACE
_RANGE: ("обхват" | "range") _SPACE?
_TO: _SPACE ("до" | "to") _SPACE
_STEP: "стъпка" | "step"
_ELIF: _SPACE? ("иначе ако" | "elif") _SPACE
_INPUT: ("въвеждане" | "input")
_OR: _SPACE ("или" | "or") _SPACE
_WHILE: ("докато" | "while") _SPACE
_LENGTH: "дължина" | "length"
_COLOR : ("цвят" | "color") _SPACE?
Loading

0 comments on commit 47facec

Please sign in to comment.