Навигация
Поддержать материально
Steam Greenlight

Логотипы
Медальки
Гость
Имя

Пароль



Вы не зарегистрированны?
Нажмите здесь для регистрации.

Забыли пароль?
Запросите новый здесь.
Темы форума
185 - RPG
9.02.2024
 Vaskrol
В каком банке открыт…
24.01.2024
 Darthman
185 - ?
30.12.2023
 Mefistofel
TESTAMENT - Тактичес…
15.11.2023
 KregHek
WoL
13.10.2023
 Darthman
RES - Движок для пик…
27.09.2023
 rimush
177 - One Button Str…
20.09.2023
 VoroneTZ
JS 13k contest
13.09.2023
 Mefistofel
184 - Arcade II
14.08.2023
 tiger1025
184 - ?
14.07.2023
 Kaps
Сейчас на сайте
Гостей: 2
На сайте нет зарегистрированных пользователей

Пользователей: 1,788
новичок: svetalebedeva199
Обсуждение «Парсинг строк.»
Страница 1 из 2 1 2 >
Shirson
Avatar пользователя

Опубликовано 04.11.2012 05:57 (11 лет назад)    #
Коллеги, просветите.
Как реализуется парсинг строк, для сценариев, модов и пр. Что-то посложнее Key=Value?
Вот в майнкрафте есть консоль, есть модуль управления, всё это вопринимает кучу команд, написаных от руки.
В том же X@COM система модов хавает вот такую красоту



Как это делается? У меня устойчивое впечатление, что все вокруг знают этот секрет и только я, как лох, пурхаюсь с pos и уже тот же парсинг BBCode превращается в Ад и Ужас.
Я знаю, что на свете существуют регулярные выражения, скажем, но всё явно не так просто.
Как парсят несложные скрипты? Как распарсить что-то вроде форумного BBCоde? Как реализуется распозначание сколько-нибудь сложных команд из игровой консоли, например? Как это сделать из Делфи (6)? Подскажите направление, в котором копать/искать.
Dan
Avatar пользователя

Опубликовано 04.11.2012 07:26 (11 лет назад)    #
у меня есть свой простенький парсер, хватает для любых целей.
http://gen2gdk.com/files/project1.rar
если интересно могу дать код

редакция от Dan, 04.11.2012 07:29

Shirson
Avatar пользователя

Опубликовано 04.11.2012 08:08 (11 лет назад)    #
Мне как раз хочется понять КАК это делается. Код это хорошо, а еще в добавок теории бы.
Mefistofel
Инженер‑космогоник
Avatar пользователя

Опубликовано 04.11.2012 10:08 (11 лет назад)    #
К сожалению не могу претендовать на профессионализм, так как я писал только парсеры для XML, JSON, арифметических и условных выражений и нескольких форматов 3д моделей. Еще были шаблоны для web страниц на php.
Я думаю все обстоит именно так плохо как ты думаешь.
Суть парсинга - лишь преобразование в удобный программе формат из удобного для чтения/хранения формата.
Практически всегда парсинг - это просто разбиение на составные элементы(списком либо деревом если есть иерархия) на основе спец символов и анализ кусочков - поиск токенов, команд, тэгов и т.д.
Делфи не очень к этому приспособлен в силу не самой крутой работы со строками(впрочем сторонние компоненты решают, да и в последних версиях все гораздо круче), но меня в конце концов все сводится к посимвольному перебору строки.
Регулярные выражения - очень удобная штука, хотя в парсинге имхо это скорее вспомогательный инструмент для поиска и замены. Основной принцип остается таким же. В php регулярки позволяли решать проблемы целиком в силу специфики - там и входные данные и результат - текст с тегами, свой формат представления просто не нужен.
Вот хороший пример, шаблоны я делал очень похоже
http://myrusakov.ru/php-parsing-bb.html
По поводу конкретно BBCode и других частных случаев есть много статей.
Самая крутая теория, которую я знаю, находится в книжке «Компиляторы:принципы, технологии и инструменты» - к сожалению я только начал ее осиливать.
Darthman
Древний организм
Avatar пользователя

Опубликовано 04.11.2012 11:08 (11 лет назад)    #
В кратце в теории - код разбирается анализатором на токены, где каждое слово или цифра разделяется и записывается в последовательный список (как пример) с определением типа токена (команда, переменная, число, вызов функции). Далее уже по разобранному анализатором коду идет выполнение, которое определить может только тот, кто придумал этот язык, разумеется.

Лучше всего из всех здесь присуствующих объяснит тему Дож, так как написал и свой язык программирования и даже компилятор (если не ошибаюсь. Ну написал же уже надеюсь? :) )

В целом анализатор писал и я, когда мне требовалась умная подсветка и автодополнения кода, когда писал свой quadshade.
Shirson
Avatar пользователя

Опубликовано 05.11.2012 14:42 (11 лет назад)    #
Понятно. Безрадостно, но ожидаемо :)

Dan, дай пожалуйста исходник к своему парсеру, похоже, мне этого тоже должно хватить.
Dan
Avatar пользователя

Опубликовано 05.11.2012 15:34 (11 лет назад)    #
TG2Parser = class
private
_Position: Integer;
_Text: array of AnsiChar;
_Comment: array of array[0..1] of AnsiString;
_CommentLine: array of AnsiString;
_String: array of AnsiString;
_Symbols: array of AnsiString;
_KeyWords: array of AnsiString;
_CaseSensitive: Boolean;
function GetText: AnsiString;
function GetLen: Integer; inline;
public
property Text: AnsiString read GetText;
property Len: Integer read GetLen;
property Position: Integer read _Position write _Position;
constructor Create(const ParseText: AnsiString; const CaseSensitive: Boolean = False); virtual;
destructor Destroy; override;
procedure AddComment(const CommentStart, CommentEnd: AnsiString);
procedure AddCommentLine(const CommentLine: AnsiString);
procedure AddString(const StringStartEnd: AnsiString);
procedure AddSymbol(const Symbol: AnsiString);
procedure AddKeyWord(const KeyWord: AnsiString);
procedure SkipSpaces;
function Read(const Count: Integer): AnsiString; overload;
function Read(const Pos: Integer; const Count: Integer): AnsiString; overload;
function IsAtSymbol: Integer;
function IsAtKeyword: Integer;
function IsAtCommentLine: Integer;
function IsAtCommentStart: Integer;
function IsAtCommentEnd: Integer;
function IsAtString: Integer;
function IsAtEOF: Boolean;
function NextToken(var TokenType: TG2TokenType): AnsiString;
end;

...

//TG2Parser BEGIN
function TG2Parser.GetText: AnsiString;
begin
SetLength(Result, Length(_Text));
Move(_Text[0], Result[1], Length(_Text));
end;

function TG2Parser.GetLen: Integer;
begin
Result := Length(_Text);
end;

constructor TG2Parser.Create(const ParseText: AnsiString; const CaseSensitive: Boolean = False);
begin
inherited Create;
_CaseSensitive := CaseSensitive;
SetLength(_Text, Length(ParseText));
Move(ParseText[1], _Text[0], Length(ParseText));
_Position := 0;
end;

destructor TG2Parser.Destroy;
begin
inherited Destroy;
end;

procedure TG2Parser.AddComment(const CommentStart, CommentEnd: AnsiString);
begin
SetLength(_Comment, Length(_Comment) + 1);
_Comment[High(_Comment)][0] := CommentStart;
_Comment[High(_Comment)][1] := CommentEnd;
end;

procedure TG2Parser.AddCommentLine(const CommentLine: AnsiString);
begin
SetLength(_CommentLine, Length(_CommentLine) + 1);
_CommentLine[High(_CommentLine)] := CommentLine;
end;

procedure TG2Parser.AddString(const StringStartEnd: AnsiString);
begin
SetLength(_String, Length(_String) + 1);
_String[High(_String)] := StringStartEnd;
end;

procedure TG2Parser.AddSymbol(const Symbol: AnsiString);
begin
SetLength(_Symbols, Length(_Symbols) + 1);
_Symbols[High(_Symbols)] := Symbol;
end;

procedure TG2Parser.AddKeyWord(const KeyWord: AnsiString);
begin
SetLength(_KeyWords, Length(_KeyWords) + 1);
_KeyWords[High(_KeyWords)] := KeyWord;
end;

procedure TG2Parser.SkipSpaces;
begin
while (_Position < Len)
and (
(_Text[_Position] = ' ')
or (_Text[_Position] = #$D)
or (_Text[_Position] = #$A)
) do
Inc(_Position);
end;

function TG2Parser.Read(const Count: Integer): AnsiString;
var c: Integer;
begin
if Count + _Position > Len then
c := Len - _Position
else
c := Count;
SetLength(Result, c);
Move(_Text[_Position], Result[1], c);
Inc(_Position, c);
end;

function TG2Parser.Read(const Pos: Integer; const Count: Integer): AnsiString;
var c: Integer;
begin
if Count + Pos > Len then
c := Len - Pos
else
c := Count;
if c <= 0 then
begin
Result := '';
Exit;
end;
SetLength(Result, c);
Move(_Text[Pos], Result[1], c);
end;

function TG2Parser.IsAtSymbol: Integer;
var i, j: Integer;
var Match: Boolean;
begin
for i := 0 to High(_Symbols) do
begin
Match := True;
for j := 0 to Length(_Symbols[i]) - 1 do
if (_CaseSensitive and (_Text[_Position + j] <> _Symbols[i][j + 1]))
or (not _CaseSensitive and (LowerCase(_Text[_Position + j]) <> LowerCase(_Symbols[i][j + 1]))) then
begin
Match := False;
Break;
end;
if Match then
begin
Result := i;
Exit;
end;
end;
Result := -1;
end;

function TG2Parser.IsAtKeyword: Integer;
var i, j: Integer;
var Match: Boolean;
begin
for i := 0 to High(_KeyWords) do
begin
Match := True;
for j := 0 to Length(_KeyWords[i]) - 1 do
if (_CaseSensitive and (_Text[_Position + j] <> _KeyWords[i][j + 1]))
or (not _CaseSensitive and (LowerCase(_Text[_Position + j]) <> LowerCase(_KeyWords[i][j + 1]))) then
begin
Match := False;
Break;
end;
if Match then
begin
Result := i;
Exit;
end;
end;
Result := -1;
end;

function TG2Parser.IsAtCommentLine: Integer;
var i, j: Integer;
var Match: Boolean;
begin
for i := 0 to High(_CommentLine) do
begin
Match := True;
for j := 0 to Length(_CommentLine[i]) - 1 do
if (_CaseSensitive and (_Text[_Position + j] <> _CommentLine[i][j + 1]))
or (not _CaseSensitive and (LowerCase(_Text[_Position + j]) <> LowerCase(_CommentLine[i][j + 1]))) then
begin
Match := False;
Break;
end;
if Match then
begin
Result := i;
Exit;
end;
end;
Result := -1;
end;

function TG2Parser.IsAtCommentStart: Integer;
var i, j: Integer;
var Match: Boolean;
begin
for i := 0 to High(_Comment) do
begin
Match := True;
for j := 0 to Length(_Comment[i][0]) - 1 do
if (_CaseSensitive and (_Text[_Position + j] <> _Comment[i][0][j + 1]))
or (not _CaseSensitive and (LowerCase(_Text[_Position + j]) <> LowerCase(_Comment[i][0][j + 1]))) then
begin
Match := False;
Break;
end;
if Match then
begin
Result := i;
Exit;
end;
end;
Result := -1;
end;

function TG2Parser.IsAtCommentEnd: Integer;
var i, j: Integer;
var Match: Boolean;
begin
for i := 0 to High(_Comment) do
begin
Match := True;
for j := 0 to Length(_Comment[i][1]) - 1 do
if (_CaseSensitive and (_Text[_Position + j] <> _Comment[i][1][j + 1]))
or (not _CaseSensitive and (LowerCase(_Text[_Position + j]) <> LowerCase(_Comment[i][1][j + 1]))) then
begin
Match := False;
Break;
end;
if Match then
begin
Result := i;
Exit;
end;
end;
Result := -1;
end;

function TG2Parser.IsAtString: Integer;
var i, j: Integer;
var Match: Boolean;
begin
for i := 0 to High(_String) do
begin
Match := True;
for j := 0 to Length(_String[i]) - 1 do
if (_CaseSensitive and (_Text[_Position + j] <> _String[i][j + 1]))
or (not _CaseSensitive and (LowerCase(_Text[_Position + j]) <> LowerCase(_String[i][j + 1]))) then
begin
Match := False;
Break;
end;
if Match then
begin
Result := i;
Exit;
end;
end;
Result := -1;
end;

function TG2Parser.IsAtEOF: Boolean;
begin
Result := _Position >= Len;
end;

function TG2Parser.NextToken(var TokenType: TG2TokenType): AnsiString;
var i: Integer;
var b: Boolean;
var Str: AnsiString;
begin
Result := '';
TokenType := ttEOF;
SkipSpaces;
if _Position >= Len then
Exit;
i := IsAtCommentStart;
while i > -1 do
begin
Inc(_Position, Length(_Comment[i][0]));
while (_Position < Len - Length(_Comment[i][1]))
and (IsAtCommentEnd <> i) do
Inc(_Position);
Inc(_Position, Length(_Comment[i][1]));
SkipSpaces;
i := IsAtCommentStart;
end;
i := IsAtCommentLine;
while i > -1 do
begin
Inc(_Position, Length(_CommentLine[i]));
while (_Position < Len)
and (_Text[_Position] <> #$D)
and (_Text[_Position] <> #$A) do
Inc(_Position);
SkipSpaces;
i := IsAtCommentLine;
end;
i := IsAtString;
if i > -1 then
begin
TokenType := ttString;
Inc(_Position, Length(_String[i]));
while (_Position <= Len - Length(_String[i]))
and (IsAtString <> i) do
begin
Result := Result + _Text[_Position];
Inc(_Position);
end;
if _Position <= Len - Length(_String[i]) then
Inc(_Position, Length(_String[i]));
Exit;
end;
i := IsAtSymbol;
if i > -1 then
begin
TokenType := ttSymbol;
Result := _Symbols[i];
Inc(_Position, Length(_Symbols[i]));
Exit;
end;
b := True;
while b do
begin
Result := Result + _Text[_Position];
Inc(_Position);
if _Position >= Length(_Text) then
b := False;
if b and (
(_Text[_Position] = ' ')
or (_Text[_Position] = #$D)
or (_Text[_Position] = #$A)
) then
b := False;
if b then
begin
i := IsAtSymbol;
if i > -1 then
b := False;
end;
end;
if Length(Result) > 0 then
begin
if StrToIntDef(Result, 0) = StrToIntDef(Result, 1) then
begin
TokenType := ttNumber;
Exit;
end;
for i := 0 to High(_KeyWords) do
if LowerCase(_KeyWords[i]) = LowerCase(Result) then
begin
TokenType := ttKeyword;
Result := _KeyWords[i];
Exit;
end;
TokenType := ttWord;
end;
end;
//TG2Parser END


инициалмзация у меня в примере такая:
Code := Memo1.Lines.Text;
Parser := TG2Parser.Create(Code);
Parser.AddComment('{', '}');
Parser.AddComment('(*', '*)');
Parser.AddCommentLine('//');
Parser.AddString('''');
Parser.AddSymbol(':=');
Parser.AddSymbol('=');
Parser.AddSymbol('+');
Parser.AddSymbol('-');
Parser.AddSymbol('*');
Parser.AddSymbol('/');
Parser.AddSymbol('(');
Parser.AddSymbol(')');
Parser.AddSymbol('[');
Parser.AddSymbol(']');
Parser.AddSymbol('.');
Parser.AddSymbol(';');
Parser.AddKeyWord('begin');
Parser.AddKeyWord('end');


при нажатии кнопки происходит вот это:
Edit1.Text := Parser.NextToken(TokenType);
case TokenType of
ttEOF: Edit1.Text := Edit1.Text + ' (EOF)';
ttError: Edit1.Text := Edit1.Text + ' (Error)';
ttString: Edit1.Text := Edit1.Text + ' (String)';
ttSymbol: Edit1.Text := Edit1.Text + ' (Symbol)';
ttWord: Edit1.Text := Edit1.Text + ' (Word)';
ttKeyword: Edit1.Text := Edit1.Text + ' (Keyword)';
ttNumber: Edit1.Text := Edit1.Text + ' (Number)';
end;


компилируется это в fpc, но я думаю в делфи тоже проблем не будет.

редакция от Dan, 05.11.2012 15:40

Darthman
Древний организм
Avatar пользователя

Опубликовано 05.11.2012 16:06 (11 лет назад)    #
Мда, за качество кода Дан, тебе руки отрывать надо :)
Dan
Avatar пользователя

Опубликовано 05.11.2012 16:39 (11 лет назад)    #
хороший и красивый у меня код=) не нравятся мне ваши маковские принципы писать сочинения в именах переменных.
Shirson
Avatar пользователя

Опубликовано 05.11.2012 17:00 (11 лет назад)    #
Пасиб, буду копаться.
Я, правда, свой разметочный парсер как раз допилил (узнав, что нет магического слова и всё надо делать руками, что сподвигло :) ) за одно и с твоим варианто разберусь

редакция от Shirson, 05.11.2012 17:01

Zer0
Avatar пользователя

Опубликовано 05.11.2012 17:09 (11 лет назад)    #
Писать парсеры ручками дело муторное, можно фгуглить yacc или bison. Однако они не настолько удобные как более модерновые тулзы.
Раз: http://goldparser.org/ Я сам пользовался, сделал интерпретатор си-подобного скрипотового языка, имеет готовые генераторы и парсеры под дельфу.
Два: http://www.antlr.org/ Этим бользуются те кто е&*(шат по хардкору.
Dj_smart
Avatar пользователя

Опубликовано 05.11.2012 23:09 (11 лет назад)    #
Мне больше всего понравился формат файлов как в питоне с отступами по ТАБ-ам (а еще это реализовано в игре Cortex Command). Формат такой:
ParentElement
    ChildElement = LOL
        Name = Вася
        TypeOf = Circle
    OtherChild = WUT
        Name = Петя
        TypeOf

Фишка в простоте, фунциональности и удобочитаемости. И парсер написать нетрудно.
Doj
Avatar пользователя

Опубликовано 06.11.2012 07:16 (11 лет назад)    #
Zer0 дело говорит, один раз опробуешь мощь генераторов LALR или LL(k), и больше нет проблемы с парсингом, будете парсить всё на свете силой мысли. Чтобы понять как оно устроено внутри, можно почитать Книгу дракона, но на практике это вовсе не обязательно.

В совсем простых случаях свои парсеры писать легко. У меня с самого начала геймдевелоперской деятельности была парочка функций, которыми я орудовал при разборе простых штук и их мне хватало для большого спектра конфигов
// Убивает все пробелы слева и справа
Trim(S): String;
// Разбивает строку S на две части первой встретившейся подстрокой Separator
// Заносит то, что левее, в Left, а то, что правее, в Right
// Вернёт False, если Separator не найден в строке; в Left окажется S, а в Right пустота
ParseBinary(S, Separator, out Left, out Right): Boolean;
// Разбивает строку на части разделителем Separator (легко реализуется через ParseBinary)
ParseList(S, Separator, out List): Boolean;


В конкурсе depict даже микроскриптовик был, вот начало описания первого уровня
Clear
@Level = TBackground!hello/blabla/mode=1, TPhys, TXMMusic, TOnGameOver
@TPlayer = TBody, TSubmRender, TSubmForm, TSubm, TPositer!x=160/y=120, THp!hp=25/max=25, THpBar!x=20/y=40/w=100/h=5/r=1/g=0/b=0, THpGameOver
@Player = TMouseCatcher, TBody, TLineBar!x=200/y=40/w=100/h=5/r=0/g=0/b=1
@Glava = TRenderAppear!time=4000, TRenderGlava1!time=4000

@Enemy = TGameTimer!time=7000/scr=@TEnemy = TCicloidForm; TPositer!x:270_y:240; TBody; TVelocity!x:-1_y:-1; TLineMirrored; TCicloidRender; TDamagable!aim:TSubm_damage:1; TOutKiller

@Enemy = TGameTimer!time=9000/scr=@TEnemy = TCicloidForm; TPositer!x:230_y:240; TBody; TVelocity!x:-1_y:-1; TLineMirrored; TCicloidRender; TDamagable!aim:TSubm_damage:1; TOutKiller

@Enemy = TGameTimer!time=11000/scr=@TEnemy = TCicloidForm; TPositer!x:170_y:240; TBody; TVelocity!x:-0.1_y:-1; TLineMirrored; TCicloidRender; TDamagable!aim:TSubm_damage:1; TOutKiller

@Enemy = TGameTimer!time=13000/scr=@TEnemy = TCicloidForm; TPositer!x:270_y:240; TBody; TVelocity!x:-1_y:-1; TLineMirrored; TCicloidRender; TDamagable!aim:TSubm_damage:1; TOutKiller

@Enemy = TGameTimer!time=15000/scr=@TEnemy = TCicloidForm; TPositer!x:60_y:240; TBody; TVelocity!x:1_y:-1; TLineMirrored; TCicloidRender; TDamagable!aim:TSubm_damage:1; TOutKiller

@Enemy = TGameTimer!time=17000/scr=@TEnemy = TCicloidForm; TPositer!x:200_y:240; TBody; TVelocity!x:-0.1_y:-1; TLineMirrored; TCicloidRender; TDamagable!aim:TSubm_damage:1; TOutKiller

@Enemy = TGameTimer!time=19000/scr=@TEnemy = TCicloidForm; TPositer!x:160_y:240; TBody; TVelocity!x:-0.1_y:-1; TLineMirrored; TCicloidRender; TDamagable!aim:TSubm_damage:1; TOutKiller

@Enemy = TGameTimer!time=21000/scr=@TEnemy = TCicloidForm; TPositer!x:100_y:240; TBody; TVelocity!x:0.3_y:-1; TLineMirrored; TCicloidRender; TDamagable!aim:TSubm_damage:1; TOutKiller

@Enemy = TGameTimer!time=23000/scr=@TEnemy = TCicloidForm; TPositer!x:200_y:240; TBody; TVelocity!x:-0.1_y:-1; TLineMirrored; TCicloidRender; TDamagable!aim:TSubm_damage:1; TOutKiller

@CR = TGameTimer!time=26000/scr=@CR = TCicloidRespawnFront!count:7_interval:625; TPositer!x:80_y:240
@CR = TGameTimer!time=31000/scr=@CR = TCicloidRespawnFront!count:7_interval:625; TPositer!x:240_y:240
...


Дешёвый, но универсальный метод своего красивого и удобного конфига — это использовать XML. Готовых парсеров дофига, свой написать нетрудно: последовательно поглощаем символы слева направо и применяем в нужные моменты рекурсию, на выходе получаем дерево, которое уже легко обходить программно.

редакция от Doj, 06.11.2012 07:25

Zer0
Avatar пользователя

Опубликовано 06.11.2012 10:37 (11 лет назад)    #
Doj написал:
Дешёвый, но универсальный метод своего красивого и удобного конфига — это использовать XML. Готовых парсеров дофига, свой написать нетрудно: последовательно поглощаем символы слева направо и применяем в нужные моменты рекурсию, на выходе получаем дерево, которое уже легко обходить программно.


Добавлю что XML в силу своей брутальности не совсем приспособлен для лекого редактирования и часто заменяется на JSON, а ценители прекрасного сразу вспомнят YAML. Они оба достаточно просто парсятся.

Однако использовать в релизе plain-text, который еще и парсить надо не рекомендуется, страдает и время загрузки и появляется дырка для желающих своими ручками что-то подправить. Заменой становится BSON файлик который генерится из JSON простеньким парсером-конвертором.
Doj
Avatar пользователя

Опубликовано 06.11.2012 10:46 (11 лет назад)    #
JSON и YAML хороши.

Если говорить о скорости, то ещё можно вспомнить про protobuf, который тоже умеет из plain-text превращаться в бинарный формат, да ещё и генерировать код для его программного чтения.

Но всё же конфиги обычно набираются вручную, парсинг маленького XML-файла трудно назвать брутальным.

редакция от Doj, 06.11.2012 10:46

0nni
Avatar пользователя

Опубликовано 06.11.2012 12:25 (11 лет назад)    #
Doj, хехе я тоже на один из конкурсов писал свой простенький интерпретатор, в результате карты как-то так выглядели:
4 "blood" 4 1 insert
4 "blood" 4 4 insert
4 "blood" 5 1 insert
4 "blood" 5 4 insert
"" "3 \"blood\" 4 1 insert
3 \"blood\" 4 4 insert
3 \"blood\" 5 1 insert
3 \"blood\" 5 4 insert" 0 1 timer
"" "2 \"blood\" 4 1 insert
2 \"blood\" 4 4 insert
2 \"blood\" 5 1 insert
2 \"blood\" 5 4 insert" 0 2 timer
"Вот тебе в награду четыре помошницы" msg
1 4 do
dup !a
"fangirl" "" "\"Похоже твою напарницу убили.\" msg" 0 @a InsertEx 1 !Team
loop
2 "MapZombiesCount" !Global
"zombie" "" "onDie" 2 2 insertEx drop
"zombie" "" "onDie" 2 3 insertEx drop
3 2 solid
3 3 solid
1 2 solid
1 3 solid
3 5 clear
3 0 clear
2 "MapZombiesCount" !Global
"zombie" "" "onDie" 2 2 insertEx drop
"zombie" "" "onDie" 2 3 insertEx drop
0 persons 0 2 move
"+50" "" "next+50" 0 0 InsertEx
:next+50
"" "\"+50\" \"\" \"next+50\" 0 0 InsertEx" 0 15 Timer
;
"shoot" "" "nextshoot" 2 0 InsertEx
:nextshoot
"" "\"shoot\" \"\" \"nextshoot\" 2 0 InsertEx" 0 25 Timer
;
"gun" "" "nextgun" 0 5 InsertEx
:nextgun
"" "\"gun\" \"\" \"nextgun\" 0 5 InsertEx" 0 20 Timer
;
"rocket" "" "nextrocket" 2 5 InsertEx
:nextrocket
"" "\"rocket\" \"\" \"nextrocket\" 2 5 InsertEx" 0 30 Timer
;
:onDie
"MapZombiesCount" @Global 1 - dup "MapZombiesCount" !Global
0 =
if
3 2 clear
3 3 clear
"Поздравляю, ты убил обоих главарей, но к ним пришла подмога!" msg
1 "fangirl" 5 0 insert
1 "fangirl" 5 5 insert
1 "fangirlex" 4 0 insert
1 "fangirlex" 4 5 insert
5 2 enable
then
;


А по теме, парсер на конечных автоматах (что бы искал отдельные слова, символы и строки (последовательности в кавычках) и выдавал в виде списка, написать несложно, или лучше использовать готовый.
RichDad
Avatar пользователя

Опубликовано 06.11.2012 14:11 (11 лет назад)    #
Shirson написал:
В том же X@COM система модов хавает вот такую красоту

Я сейчас пересел на iOS разработку интерфейса. Там такие конструкции везде. По сути вызов функции идет вот так:
[object FunctionWithParam1:par1 Param2:par2 Param3:par3]


На приведенной тобой картинке (см. первый пост) как раз примерно такая запись и есть. Например вот такой конфиг читался бы мной как книга:
CreateNewWeaponWithName:"Bazooka" Power:100 Radius:150
CreateNewWeaponWithName:"Pistol" Power:5 Radius:100


Можно и делать придури вроде такой:
ChangeWeaponWithName:"Pistol" SetPower:3 Radius:110

(заменит параметры уже созданного оружия)


В Obj-C так нельзя, но в конфиге, думаю, можно реализовать, чтобы параметры шли не в заданном порядке. Пример:
CreateNewWeaponWithName:"Bazooka" Power:100 Radius:150
CreateNewWeaponWithName:"Pistol" Radius:100 Power:5
CreateNewWeaponWithName:"Gun" Weight:1

(тут я подразумеваю, что у оружия есть много параметров, но заполнять можешь лишь те, что тебе нужны - остальные пойдут по умолчанию)

Раньше я просто не знал такого метода записи, а теперь знаю. Если бы делал конфиг - скорее всего вот такой.
Shirson
Avatar пользователя

Опубликовано 06.11.2012 15:07 (11 лет назад)    #
Dj_smart написал:
Мне больше всего понравился формат файлов как в питоне с отступами по ТАБ-ам (а еще это реализовано в игре Cortex Command). Формат такой:
ParentElement
    ChildElement = LOL
        Name = Вася
        TypeOf = Circle
    OtherChild = WUT
        Name = Петя
        TypeOf

Фишка в простоте, фунциональности и удобочитаемости. И парсер написать нетрудно.


Как реализуется парсинг строк, для сценариев, модов и пр. Что-то посложнее Key=Value
:)
Ты привёл как раз самое простое - ключ=значение. Для него и парсинг, как таковой, не нужен и табуляция не более чем свистелка-перделка (иначе, завязка функциональности на форматирование и звиздец).
Это у меня уже есть, я с этого стартовал.

Doj написал:
// Убивает все пробелы слева и справа
Trim(S): String;
// Разбивает строку S на две части первой встретившейся подстрокой Separator
// Заносит то, что левее, в Left, а то, что правее, в Right
// Вернёт False, если Separator не найден в строке; в Left окажется S, а в Right пустота
ParseBinary(S, Separator, out Left, out Right): Boolean;
// Разбивает строку на части разделителем Separator (легко реализуется через ParseBinary)
ParseList(S, Separator, out List): Boolean;
Примерно суть понятна, спасибо, буду думать дальше.

RichDad написал:
Я сейчас пересел на iOS разработку интерфейса. Там такие конструкции везде. конфиг - скорее всего вот такой.
Понимаешь, вопрос стоит не "где так делается", вопрос стоит "КАК это реализовано" :)
Zer0
Avatar пользователя

Опубликовано 06.11.2012 16:26 (11 лет назад)    #
Совет - не стоит ожидать чудес, обычно первый проход делается что называется "в лоб", брутфорсом.

Что оптимизируется:
* identifiers после того как выделены в тексте, через hash map преобразуются в айдишники чтоб в свичах/кейсах было удобнее с ними работать;

* обычно в парсинге исходный текст не разбивается буквально на подстроки которые хранятся в виде копий в памяти, а преобразуется пары (адрес, длинна) в буффере, это позволяет экономить память и немного ускоряет процесс из-за отсуствия необходимости в копировании и строковых аллолкаций (обычно правильно реализованные вектора делают преаллокацию и там выделений получается в сумме меньше);

Что советую заценить: https://github.com/Kirill/simplexml эта либа пропитана очень неплохим стилем и в целом полезна для общего развития. В свое время многому меня научила :)
JKot
Avatar пользователя

Опубликовано 09.11.2012 04:17 (11 лет назад)    #
Не понимаю зачем выдумывать свой формат? На работе пользуемся YAML для описания всех обьектов на сцене, выглядит как-то так:
http://pastebin.com/raw.php?i=9syhQiSd

что ещё нужно?

редакция от JKot, 09.11.2012 04:20

Страница 1 из 2 1 2 >
Перейти на форум:
Конкурсы
Открытые конкурсы:
Активных нет
Недавние конкурсы:
 185 - RPG XII
 184 - Arcade II
 183 - Novel
 182 - RPG XI
 181 - Pixel Craft 128
 Все конкурсы
Случайная игра
Мини-чат
Вам необходимо залогиниться.

Архив чата

25,320,723 уникальных посетителей

Создано на базе русской версии PHP-Fusion copyright © 2003-2006 by Nick Jones.
Released as free software under the terms of the GNU/GPL license.