Самодокументирование парсерного кода
Самодокументирование парсерного кодаМорда: СтандартнаяСераяЗеленая Главная / Портфель / Самодокументирование парсерного кода Запомнить № 23:
Самодокументирование парсерного кодаОпубликовано:
14 сентября 2003Изменено:
16 сентября 2003 | Общепризнанно, — что самодокументированная программа, это очень полезная и нужная вещь. Идея эта совсем не нова — ещё в 1975 году Ф. Брукс, в своём, Мифическом человеко-месяце, высказывал эту идею. Для классических языков программирования инструменты для составления документации по программам прямо из исходных текстов существуют уже давно, тот же Doxygen, однако для такого молодого и не очень, надо сказать, популярного языка Парсер, таких вещей пока нет.Александр Петросян (это автор 3-го парсера) высказал эту идею уже давно, однако ни у кого руки до сих пор не доходили до создания простецкого кода, извлекающего информацию из исходных текстов и создающих простенькую документацию по коду. На самом деле и у меня бы не дошли, однако я стал запутываться в собственном коде без нормальной документации, и значит пришла пора приводить это дело в порядок.Для тех кто не в танке — мне не досуг разбираться с Doxygen и я лучше сделаю ещё один велосипед, тем более, что для меня пока это проще.Итак, Вашему вниманию предлагается набор методов:Извлекающих информацию из файлов с парсерным кодомВыводящих информацию (если есть) о имени класса, описание класса информацию о родительском классе.Выводящих информацию о методах определённых в этих файлах.Выводящих информацию о параметрах (если они есть), передаваемых этим методам.Выводящих информацию о локальных переменных методов.Выводящих описания методовКод работает очень простым способом — он сканирует все каталоги, определённые в переменной $CLASS_PATH и получает информацию обо всех .html и .p файлах находящихся в этих каталогах. Вывод осуществляется в 2 колонки — в левой, в зависимости от установленной куки view:В виде, — сначала список каталогов из $CLASS_PATH а затем, для выбранного каталога, список его файлов.В виде иерархии классов.В правой колонке выводится информация о выбранном файле с классом.Мне этот способ (сканирование каталогов из $CLASS_PATH) подходит — т.к. у меня весь код сосредоточен в моих классах и операторах (находящихся в каталогах определённых $CLASS_PATH) а класс MAIN на сайте составляют только конфигурационный auto.p, auto.p находящийся в корне веб-пространства и index.html находящийся там же. В случае если это не так (код раскидан по куче html/p файлам в веб-пространстве) этот способ работать не будет, или будет, если все эти каталоги добавить в $CLASS_PATH.Код методов:
##############################
# Вывод либо списка файлов/каталогов
# либо иерархии классов
# Вызывать где-нибудь внутри <body> ... </body>
@content[]
^if(def $form:view){
$cookie:view[
$.value[^form:view.int(0)]
$.expires(365)
]
}
<table class="contentCells" style="width: 100%">
<tr>
<td style="width: 25%; vertical-align: top;">
^switchView[]
^if(!^cookie:view.int(0)){
^files[]
}{
^tree[]
}
</td>
<td style="width: 75%; vertical-align: top;">
^rem{/**
имя файла может быть пустым поэтому может возникать ошибка
чтения с диска
*/}
^try{
^classInfo[$form:path/$form:fileName]
}{
^if($exception.type eq file.access || $exception.type eq file.missing){
$exception.handled(1)
}
}
</td>
</tr>
</table>
##############################
# Документация по классу
# ВНИМАНИЕ!: В реальном коде, все переменные, используемые в методе
# кроме разумеется параметра метода
# сделать локальными. Я это не сделал по причине читабельности
# при публикации текста в вебе
@classInfo[strClass]
$polyPageClass[^file::load[text;$strClass]]
$polyPageClass[$polyPageClass.text]
$tblPageClass[^classNameAndDesc[$polyPageClass]]
# Имя класса
^if(def $tblPageClass.2){
$strPageClassName[$tblPageClass.2]
}{
$strPageClassName[MAIN]
}
# Описание класса
$strPageClassDescr[$tblPageClass.1]
# Родительский класс (если есть)
$strBaseClass[^baseClass[$polyPageClass]]
# замена шарпов на <br/>
$strPageClassDescr[^strPageClassDescr.match[#][g]{<br/>}]
$strPageClassDescr[^strPageClassDescr.match[<br/>][]{}]
# Вывод
<h1>Класс: $strPageClassName</h1>
^if(def $strBaseClass){
$hashClasses[^classesHash[]]
<h2>Родительский класс:</h2>
<a href="./?path=$hashClasses.[$strBaseClass].path&fileName=$hashClasses.[$strBaseClass].file">
$strBaseClass
</a>
}
^if(def $strPageClassDescr){
<dl>
<dt><h2>Описание:</h2></dt>
<dd>$strPageClassDescr</dd>
</dl>
}
<h2><a name="methods" id="0">Методы (коротко):</a></h2>
$tblClassMethod[^methods[$polyPageClass]]
# Список со ссылками на подробное описание метода
<ol>
$nJx(1)
^tblClassMethod.menu{
<li><a href="$request:uri#$nJx">$tblClassMethod.2</a></li>
^nJx.inc(1)
}
</ol>
<h2>Методы (подробно):</h2>
<dl>
$nJx(1)
^tblClassMethod.menu{
<dt>
<h3>
${nJx}. <a name="$tblClassMethod.2" id="$nJx">$tblClassMethod.2</a>
[<a href="$request:uri#0">К началу</a>]
</h3>
</dt>
^nJx.inc(1)
<dd>
<dl>
$strMethodDescr[$tblClassMethod.1]
$strMethodDescr[^strMethodDescr.match[#][g]{<br/>}]
$strMethodDescr[^strMethodDescr.match[<br/>][]{}]
^rem{/** Вывод описания метода (если есть) */}
^if(^strMethodDescr.length[] > 2){
<dt><h4>Описание:</h4></dt>
<dd>$strMethodDescr</dd>
}
^rem{/** Вывод списка передаваемых параметров (если есть) */}
^if(def $tblClassMethod.3){
$tblParams[^tblClassMethod.3.split[^;]]
<dt><h4>Параметры:</h4></dt>
<dd>
$nIx(1)
^tblParams.menu{
${nIx}. $tblParams.piece<br/>
^nIx.inc(1)
}
</dd>
}
^rem{/** Вывод списка локальных переменных (если есть) */}
^if(def $tblClassMethod.4){
$tblLocalVars[^tblClassMethod.4.split[^;]]
<dt><h4>Локальные переменные:</h4></dt>
<dd>
$hashLocalVars[^tblLocalVars.hash[piece]]
^tblLocalVars.menu{
- $tblLocalVars.piece<br/>
}
</dd>
}
</dl>
</dd>
}
</dl>
##############################
# Файлы и каталоги с классами
@files[][tblFilesList]
<h1>Каталоги</h1>
<ul>
<li>
^if(def $form:path){
<a href="?path=">Корень сайта</a>
}{
<strong>Корень сайта</strong>
}
</li>
^CLASS_PATH.menu{
<li>
^if($CLASS_PATH.path ne $form:path){
<a href="?path=$CLASS_PATH.path">
$CLASS_PATH.path
</a>
}{
<strong>$CLASS_PATH.path</strong>
}
</li>
}
</ul>
<h1>Файлы</h1>
$tblFilesList[^file:list[$form:path/;\.(p|html)^$]]
<ul>
^tblFilesList.menu{
<li>
^if($tblFilesList.name ne $form:fileName){
<a href="?path=$form:path&fileName=$tblFilesList.name">
$tblFilesList.name
</a>
}{
<strong>$tblFilesList.name</strong>
}
</li>
}
</ul>
##############################
# таблица всех пользовательских классов сайта
@classesTable[][tblFilesList;polyPageClass;tblClassInfo]
$result[^table::create{class file path base
MAIN index.html / }]
^CLASS_PATH.menu{
$tblFilesList[^file:list[$CLASS_PATH.path/;\.(p|html)^$]]
^tblFilesList.menu{
$polyPageClass[^file::load[text;$CLASS_PATH.path/$tblFilesList.name]]
$polyPageClass[$polyPageClass.text]
^rem{/** Определение имени класса */}
$tblClassInfo[^classNameAndDesc[$polyPageClass]]
^if(def $tblClassInfo.2){
^result.append{$tblClassInfo.2 $tblFilesList.name $CLASS_PATH.path ^baseClass[$polyPageClass]}
}
}
}
##############################
# Хэш всех пользовательских классов сайта
@classesHash[]
$result[^classesTable[]]
$result[^result.hash[class]]
##############################
# Получает текст файла класса
# Возвращает таблицу с информацией о всех методах
# определённых в файле
#
# Раскладка по столбцам
# 1 - Описание метода
# 2 - Имя метода
# 3 - Строка передаваемых параметров (если есть) разделённых ;
# 4 - Строка локальных переменных (если есть) разделённых ;
@methods[strFileText]
$result[^strFileText.match[
(?:
\#{30} # метка начала описания метода (30 шарпов)
([^^@]*?)? # описание метода
)?
(?:\n|^^) # перед @ обязательно или перевод строки или начало файла
# ставить эту конструкцию именно тут - ВАЖНО!
@([^^^$@^;()#]+?) # имя метода
\[
(.*?) # передаваемые параметры
\]
(?:
\[
(.*?) # локальные переменные
\]
)*
\s
][gx]]
##############################
# Определение имени и описания класса (если есть)
# Получает текст файла класса
# Возвращает таблицу со столбцами:
# 1 - описание класса
# 2 - имя класса
@classNameAndDesc[strFileText]
$result[^strFileText.match[
(?:
\#{30}
(.*?) # описание класса
)?
(?:\n|^^)
@CLASS\n
(.*?) # имя класса
\n
][x]]
##############################
# Определение имени родительского класса (если есть)
# Получает текст файла класса
# Возвращает строку с именем базового класса
@baseClass[strFileText]
$result[^strFileText.match[
\n
@BASE
\n
(.+?) # имя базового класса
\n
][x]]
$result[$result.1]
##############################
# формирование элемента дерева классов
@prnTreeItem[tblItem;strChilds]
$result[
<li>
^if($tblItem.file ne $form:fileName){
<a href="?path=$tblItem.path&fileName=$tblItem.file" style="font-size: 90%">
$tblItem.class
</a>
}{
<strong>$tblItem.class</strong>
}
</li>
<ul>$strChilds</ul>
]
##############################
# Хэш всех пользовательских классов сайта
# с ключом по род. классу
# Параметры:
# $tblItems - таблица с элементами дерева
# $strParent - столбец с идентификатором родителя
@createHashTree[tblItems;strParent][tblEmpty]
$tblEmpty[^table::create[$tblItems][$.limit(0)]]
$result[^hash::create[]]
^tblItems.menu{
^if(!$result.[$tblItems.[$strParent]]){
$result.[$tblItems.[$strParent]][^table::create[$tblEmpty]]
}
^result.[$tblItems.[$strParent]].join[$tblItems][$.limit(1)$.offset[cur]]
}
##############################
# Файлы и каталоги с классами
@tree[][tblClasses;hashTree]
$tblClasses[^classesTable[]]
^tblClasses.sort{class}
$hashTree[^createHashTree[$tblClasses;base]]
$result[
<h1>Иерархия классов</h1>
<ul>^prnTree[$hashTree;]</ul>
]
##############################
# формирование дерева элементов
# метод рекурсивно вызывает сам себя через вызов метода
# prnTreeItem(формирование элемента дерева)
# в $tblBrotherItems формируется список элементов одного уровня(с общим предком)
@prnTree[hashTree;strBase][tblBrotherItems]
^if($hashTree.[$strBase]){
$tblBrotherItems[$hashTree.[$strBase]]
^tblBrotherItems.menu{
^prnTreeItem[$tblBrotherItems.fields;^if($hashTree.[$tblBrotherItems.class]){^prnTree[$hashTree;$tblBrotherItems.class]}]
}
}
##############################
# Форма переключения вида
@switchView[]
<h1>Показывать</h1>
<form action="" method="post">
<input type="radio" name="view" value="0" checked> Список классов<br/>
<input type="radio" name="view" value="1" ^if(^cookie:view.int(0)){checked}> Иерархия классов<p/>
<input type="submit" value="Изменить">
</form>
Поместите этот код в понравившийся вам html файл и вызывайте метод ^content[] в примерно таком контексте:
@main[]
<html>
<head>
<style type="text/css"><!-- @import url(/CSS/standart.css); --></style>
</head>
<body>
^content[]
</body>
</html>
Где standart.css стилевая таблица с требуемым оформлением используемых в документировании тегов.Примечание: Исторически так сложилось, что у меня метка начала комментариев метода/класса — это 30 (sic!) шарпов (#) если вам это не подходит, поправьте соответствующее регулярное выражение в нужных методах (сами догадаетесь в каких).В загружаемые исходники этого сайта включён каталог /docs/ с работающим кодом этого примера и, следовательно, исходники теперь имеют документацию.<< № 22 | Содержание | № 24 >>Из последнего№ 24 Работаем с .htpasswd 08.11.2003 (Изменено: 10.01.2004)№ 23 Самодокументирование парсерного кода 14.09.2003№ 22 Работаем с RSS 21.02.2003№ 21 Топологическая сортировка 16.02.2003№ 20 Установка 3-го парсера на хостинге 350mb.ru 12.02.2003ПолезноеПрограммированиеDoxygenParser 3Parser.ruГлавная / Портфель / Самодокументирование парсерного кода Запомнить Информация о сервереАвторРегистрация/настройки
содержание | 2 | Хочешь стать лидером?
Используются технологии
uCoz