You've already forked onec_codetemplate_parser
mirror of
https://github.com/240596448/onec_codetemplate_parser.git
synced 2025-11-23 21:34:39 +02:00
выделил парсер
This commit is contained in:
@@ -1,12 +1,13 @@
|
||||
"""Программный интерфейс"""
|
||||
|
||||
from pathlib import Path
|
||||
from .core import Root, parser
|
||||
from .core import Root
|
||||
from .parser import parse
|
||||
|
||||
def parse_to_src(path: str, src: str):
|
||||
"""Парсит шаблон 1С-файла и сохраняет структуру файлов в папку"""
|
||||
text = Path(path).read_text(encoding='utf-8-sig')
|
||||
root = parser(text)
|
||||
root = parse(text)
|
||||
root.to_src(src)
|
||||
|
||||
def render_from_src(src: str, path: str):
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
"""Парсер и компилятор файлов шаблонов кода 1С в скобочной нотации"""
|
||||
|
||||
import sys
|
||||
import re
|
||||
from typing import List, Union
|
||||
from pathlib import Path
|
||||
from .repository import LeafRepository, GroupRepository, dir_items
|
||||
@@ -169,143 +167,49 @@ def src_items(path: Path|str) -> List[Union[Group, Leaf]]:
|
||||
children.append(child)
|
||||
return children
|
||||
|
||||
def parser(text: str) -> Root:
|
||||
pos = 0
|
||||
# def main():
|
||||
# if len(sys.argv) < 2:
|
||||
# print("Использование: python parse_skobkofile.py <путь_к_файлу>")
|
||||
# sys.exit(1)
|
||||
|
||||
def skip_ws():
|
||||
nonlocal pos
|
||||
while pos < len(text) and text[pos] in " \n\r\t":
|
||||
pos += 1
|
||||
# path = sys.argv[1]
|
||||
# with open(path, "r", encoding="utf-8-sig", errors="ignore") as f:
|
||||
# text = f.read()
|
||||
|
||||
def take(s: str):
|
||||
nonlocal pos
|
||||
skip_ws()
|
||||
length = len(s)
|
||||
assert text[pos:pos+length] == s, f"Ожидалось '{s}' на позиции {pos}"
|
||||
pos += length
|
||||
skip_ws()
|
||||
# root = parser(text)
|
||||
# print("\n✅ Файл успешно прочитан\n")
|
||||
# root.pretty_print()
|
||||
|
||||
def parse_value():
|
||||
nonlocal pos
|
||||
if text[pos] == '"':
|
||||
return string_value()
|
||||
else:
|
||||
return numeric_value()
|
||||
# recompiled = root.compile()
|
||||
|
||||
def string_value():
|
||||
nonlocal pos
|
||||
pos += 1
|
||||
start = pos
|
||||
while True:
|
||||
if text[pos] != '"':
|
||||
pos += 1
|
||||
elif text[pos:pos+2] == '""':
|
||||
pos += 2
|
||||
else:
|
||||
break
|
||||
s = text[start:pos]
|
||||
pos += 1
|
||||
return s
|
||||
# if recompiled == text:
|
||||
# print("✅ Файл успешно скомпилирован и совпадает с исходником")
|
||||
# else:
|
||||
# # запись обратно в контрольный файл
|
||||
# output_path = path + ".out"
|
||||
# with open(output_path, "w", encoding="utf-8-sig") as f:
|
||||
# f.write(recompiled)
|
||||
|
||||
def numeric_value():
|
||||
nonlocal pos
|
||||
m = re.match(r"-?\d+", text[pos:])
|
||||
if not m:
|
||||
raise ValueError(f"Ожидалось число на позиции {pos}")
|
||||
val = m.group(0)
|
||||
pos += len(val)
|
||||
return int(val)
|
||||
# print("❌ Файл успешно скомпилирован, но не совпадает с исходником")
|
||||
# print(f"Скомпилированный файл сохранен в {output_path}")
|
||||
|
||||
def parse_children(count: int):
|
||||
nonlocal pos
|
||||
children = []
|
||||
for _ in range(count):
|
||||
take(",")
|
||||
child = parse_node()
|
||||
children.append(child)
|
||||
return children
|
||||
# source_path = 'temp/src'
|
||||
# root.to_src(source_path)
|
||||
|
||||
def parse_node() -> Union[Group, Leaf]:
|
||||
"""
|
||||
Парсит один объект — либо группу, либо лист
|
||||
{ count, { "Имя", флаг1, флаг2, "Поле4", "Поле5" } }
|
||||
"""
|
||||
nonlocal pos
|
||||
take("{")
|
||||
count = numeric_value()
|
||||
take(",")
|
||||
take("{")
|
||||
name = parse_value()
|
||||
take(",")
|
||||
is_group = numeric_value()
|
||||
take(",")
|
||||
menu_flag = numeric_value()
|
||||
take(",")
|
||||
replace = parse_value()
|
||||
take(",")
|
||||
text_val = parse_value()
|
||||
take("}")
|
||||
children = parse_children(count)
|
||||
take("}")
|
||||
# root2 = Root.from_src(source_path)
|
||||
|
||||
# Создаем правильный тип объекта в зависимости от is_group
|
||||
if int(is_group) == 1:
|
||||
return Group(name, children)
|
||||
elif int(is_group) == 0:
|
||||
return Leaf(name, menu_flag, replace, text_val)
|
||||
else:
|
||||
raise ValueError(f"Неизвестный значение флага is_group: {is_group}")
|
||||
# recompiled = root2.compile()
|
||||
|
||||
take("{")
|
||||
count = numeric_value()
|
||||
root = Root(parse_children(count))
|
||||
take("}")
|
||||
assert text[pos:] == "", f"Ожидалось конец файла, но есть остаток: {text[pos:]}"
|
||||
return root
|
||||
# if recompiled == text:
|
||||
# print("✅ Файл успешно скомпилирован и совпадает с исходником")
|
||||
# else:
|
||||
# # запись обратно в контрольный файл
|
||||
# output_path = path + ".out"
|
||||
# with open(output_path, "w", encoding="utf-8-sig") as f:
|
||||
# f.write(recompiled)
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print("Использование: python parse_skobkofile.py <путь_к_файлу>")
|
||||
sys.exit(1)
|
||||
# print("❌ Файл успешно скомпилирован, но не совпадает с исходником")
|
||||
# print(f"Скомпилированный файл сохранен в {output_path}")
|
||||
|
||||
path = sys.argv[1]
|
||||
with open(path, "r", encoding="utf-8-sig", errors="ignore") as f:
|
||||
text = f.read()
|
||||
|
||||
root = parser(text)
|
||||
print("\n✅ Файл успешно прочитан\n")
|
||||
root.pretty_print()
|
||||
|
||||
recompiled = root.compile()
|
||||
|
||||
if recompiled == text:
|
||||
print("✅ Файл успешно скомпилирован и совпадает с исходником")
|
||||
else:
|
||||
# запись обратно в контрольный файл
|
||||
output_path = path + ".out"
|
||||
with open(output_path, "w", encoding="utf-8-sig") as f:
|
||||
f.write(recompiled)
|
||||
|
||||
print("❌ Файл успешно скомпилирован, но не совпадает с исходником")
|
||||
print(f"Скомпилированный файл сохранен в {output_path}")
|
||||
|
||||
source_path = 'temp/src'
|
||||
root.to_src(source_path)
|
||||
|
||||
root2 = Root.from_src(source_path)
|
||||
|
||||
recompiled = root2.compile()
|
||||
|
||||
if recompiled == text:
|
||||
print("✅ Файл успешно скомпилирован и совпадает с исходником")
|
||||
else:
|
||||
# запись обратно в контрольный файл
|
||||
output_path = path + ".out"
|
||||
with open(output_path, "w", encoding="utf-8-sig") as f:
|
||||
f.write(recompiled)
|
||||
|
||||
print("❌ Файл успешно скомпилирован, но не совпадает с исходником")
|
||||
print(f"Скомпилированный файл сохранен в {output_path}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
# if __name__ == "__main__":
|
||||
# main()
|
||||
|
||||
98
onec_codetemplate_parser/parser.py
Normal file
98
onec_codetemplate_parser/parser.py
Normal file
@@ -0,0 +1,98 @@
|
||||
"""Парсер и компилятор файлов шаблонов кода 1С в скобочной нотации"""
|
||||
|
||||
import re
|
||||
from typing import Union
|
||||
from .core import Leaf, Group, Root
|
||||
|
||||
def parse(text: str) -> Root:
|
||||
"""Парсит текст и возвращает объект корня дерева шаблона"""
|
||||
pos = 0
|
||||
|
||||
def skip_ws():
|
||||
nonlocal pos
|
||||
while pos < len(text) and text[pos] in " \n\r\t":
|
||||
pos += 1
|
||||
|
||||
def take(s: str):
|
||||
nonlocal pos
|
||||
skip_ws()
|
||||
length = len(s)
|
||||
assert text[pos:pos+length] == s, f"Ожидалось '{s}' на позиции {pos}"
|
||||
pos += length
|
||||
skip_ws()
|
||||
|
||||
def parse_value():
|
||||
if text[pos] == '"':
|
||||
return string_value()
|
||||
else:
|
||||
return numeric_value()
|
||||
|
||||
def string_value():
|
||||
nonlocal pos
|
||||
pos += 1
|
||||
start = pos
|
||||
while True:
|
||||
if text[pos] != '"':
|
||||
pos += 1
|
||||
elif text[pos:pos+2] == '""':
|
||||
pos += 2
|
||||
else:
|
||||
break
|
||||
s = text[start:pos]
|
||||
pos += 1
|
||||
return s
|
||||
|
||||
def numeric_value():
|
||||
nonlocal pos
|
||||
m = re.match(r"-?\d+", text[pos:])
|
||||
if not m:
|
||||
raise ValueError(f"Ожидалось число на позиции {pos}")
|
||||
val = m.group(0)
|
||||
pos += len(val)
|
||||
return int(val)
|
||||
|
||||
def parse_children(count: int):
|
||||
children = []
|
||||
for _ in range(count):
|
||||
take(",")
|
||||
child = parse_node()
|
||||
children.append(child)
|
||||
return children
|
||||
|
||||
def parse_node() -> Union[Group, Leaf]:
|
||||
"""
|
||||
Парсит один объект — либо группу, либо лист
|
||||
{ count, { "Имя", флаг1, флаг2, "Поле4", "Поле5" } }
|
||||
"""
|
||||
# nonlocal pos
|
||||
take("{")
|
||||
count = numeric_value()
|
||||
take(",")
|
||||
take("{")
|
||||
name = parse_value()
|
||||
take(",")
|
||||
is_group = numeric_value()
|
||||
take(",")
|
||||
menu_flag = numeric_value()
|
||||
take(",")
|
||||
replace = parse_value()
|
||||
take(",")
|
||||
text_val = parse_value()
|
||||
take("}")
|
||||
children = parse_children(count)
|
||||
take("}")
|
||||
|
||||
# Создаем правильный тип объекта в зависимости от is_group
|
||||
if int(is_group) == 1:
|
||||
return Group(name, children)
|
||||
elif int(is_group) == 0:
|
||||
return Leaf(name, menu_flag, replace, text_val)
|
||||
else:
|
||||
raise ValueError(f"Неизвестный значение флага is_group: {is_group}")
|
||||
|
||||
take("{")
|
||||
count = numeric_value()
|
||||
root = Root(parse_children(count))
|
||||
take("}")
|
||||
assert text[pos:] == "", f"Ожидалось конец файла, но есть остаток: {text[pos:]}"
|
||||
return root
|
||||
@@ -1,4 +1,5 @@
|
||||
from onec_codetemplate_parser import core, repository
|
||||
from onec_codetemplate_parser import repository
|
||||
from onec_codetemplate_parser.parser import parse
|
||||
from tests.common import check_files_sequential
|
||||
|
||||
class TestReadSkobkofile:
|
||||
@@ -7,12 +8,12 @@ class TestReadSkobkofile:
|
||||
assert test_file_path.exists()
|
||||
|
||||
def test_01_parse_eq_compile(self, test_data):
|
||||
root = core.parser(test_data)
|
||||
root = parse(test_data)
|
||||
new_data = root.compile()
|
||||
assert new_data == test_data
|
||||
|
||||
def test_02_save_and_read(self, test_data, tmp_path):
|
||||
root = core.parser(test_data)
|
||||
root = parse(test_data)
|
||||
new_data = root.compile()
|
||||
|
||||
tmp_file = tmp_path / 'tmp.st'
|
||||
@@ -24,7 +25,7 @@ class TestReadSkobkofile:
|
||||
class TestWriteToFiles:
|
||||
|
||||
def test_white_to_src(self, test_data, temp_src):
|
||||
root = core.parser(test_data)
|
||||
root = parse(test_data)
|
||||
root.to_src(temp_src)
|
||||
|
||||
# TODO: добавить разные проверки для каждого файла
|
||||
|
||||
Reference in New Issue
Block a user