LINUX.ORG.RU

Вопрос про разбор командной строки


0

0

День добрый!

Пишу програмку, имеющую интерфейс командной строки (подобно ftp, gnuplot, gdb и т.д.). Встал вопрос о том, каким образом проводить разбор пользовательского ввода. Один из вариантов - использовать YACC/BISON, однако проблема в том, что параметры команд могут, в некоторых случаях иметь значение, совпадающее с именем другой команды. К примеру, есть команда help и есть команда show, требующая имени некоторой сущности. Тогда, вполне корректной была бы следующая конструкция:
show help.

Проблемы:
Лексемами являются не слова целиком, а отдельные символы, следовательно существенно усложняется грамматика языка. Это не смертельно, однако, хотелось бы найти более оптимальное решение.

В связи с этим такие вопросы:
1. Насколько вообще оправдан подход с использованием YACC/BISON?
2. Что делать? Оставить всё как есть или, к примеру, изменить сканер лексем, чтобы он выделял отдельные слова и изменить грамматику? Понятно, что это потребует введения "экранирования".

Всем ответившим по существу заранее благодарен.


я не совсем понял суть проблемы, но если структура твоего входного 
языка напоминает ftp, gdb и прочее, то имхо ты слишком мудришь. вообще
 можно обойтись без серьезного синтаксического анализа. 

lexer должен разбивать твои команды на поток лексем вроде: "слово", 
"число" и т.д., затем "исполнитель" этих команд проверяет первую 
лексему. Первая лексема в предложении обозначает тип комманды которую 
надо выполнить, все остальные лексемы в этой строке он старается 
интерпретировать как аргументы этой команды. Это примерно как в 
лисповских формах.

Ну например для команды copy <имяфайла1> <имяфайла2>

Lexem l = getLexem();

if (l.type() == WORD && l.content() == "copy")
{
   Lexem arg1 = getLexem();
   Lexem arg2 = getLexem();
   Lexem endOfLine = getLexem();
   
   if (endOfLine.type() == EOL) 
   {
      char * dst_filename = (arg1.type()==WORD || arg1.type()==FILENAME) ? InterpertLikeFileNaem(arg1.content()) : nil;

      char * src_filename = (arg2.type()==WORD || arg2.type()==FILENAME) ? InterpertLikeFileNaem(arg2.content()) : nil;

      if (src_filename && dst_filename) copyFile(dst_filename,src_filename);
      else error("Неправильный формат комманды copy");


   }
   else error("Неправильный формат комманды copy"); 

}

вроде правильно 

ukez
()

А точно нужен yacc/lex? Мне думается, что это реализуемо без подобных прилад. Если все-таки хочется изобразить синт. анализатор, то логичнее IMHO

1. лексический сканер распрознает слова целиком (show,help)

2. грамматика умеет это обрабатывать, например так

EXPRESSION: COMMAND ARGS '\n';

COMMAND: "help" | "show" | "hide" | ...;

тут конечно возникает довольно щекотливый момент, связанный с тем, что ARGS может содержать, как у Вас в примере, "help". Одно из решений - переключение лексического автомата после нахождения COMMAND на тупой поедатель символов, который возвращает лексему типа ARGS. Далее, полученая лексема подвергается опять-таки анализу.

Сейчас прочитал написанное, какой-то мутант получится :) Короче, лучше это набором if/else сделать, системный подход иногда слишком грамоздкий выходит :)

phoenix ★★★★
()
Ответ на: комментарий от phoenix

 можно написать тупую грамматику вида

A -> word B "\n"

B -> word B
B -> digit B
B -> ...
B -> nil

и потом анализировать подобную бодягу, если так важен системный подход :)

ukez
()
Ответ на: комментарий от ukez

Ну так yacc как раз и предлагает LALR-грамматику... На самом деле, не очень понятно, зачем писать лексический сканер и синтаксический анализатор руками, если для этого есть lex и yacc.

С другой стороны, ftp, sftp, telnet, gdb, gnuplot используют как раз "ручной" подход. И это смущает...

syomin
() автор топика
Ответ на: комментарий от phoenix

Если я правильно понял вашу мысль, то подобную грамматику использует yacc. А для решения проблемы, связанной с одинаковым написанием команд и аргументов, я склоняюсь (пока) к введению экранирования с помощью "".

syomin
() автор топика
Ответ на: комментарий от syomin

Написание такого анализатора займет не многим больше времени, чем написание граматик для yacc c событийными обработчиками.

Лексический анализатор можно сделать на flex-е если писать самому ломает, но я бы написал сам,

а то придется makefile править для того чтобы он понмал *.l и *.yy, а это геморой =)

ukez
()
Ответ на: комментарий от syomin

Насколько я понимаю, на чистом LEX лексема выделяется в независимости от контекста. Т е если написано show а потом где-то help show, то lex оба show не различит. Значит тут нужно заюзать синтаксический анализатор, в которых я увы не силен :((( . Однако можно поступить следующим образом: при обработке лексемы help устанавливаем в глобальной переменной программы флаг контекста; при обработке лексемы help проверяем текущее значение флага-контекста и в зависимости от значения этого флага выполняем соответствующее действие. Если различных контекстов в программе будет относительно мало, то такая реализация, на мой взгляд, будет целесообразной.

anonymous
()
Ответ на: комментарий от ukez

Скорее всего, всё-таки сам писать буду :).

syomin
() автор топика

>Лексемами являются не слова целиком, а отдельные символы

как опишешь так и будет

>1. Насколько вообще оправдан подход с использованием YACC/BISON?

реальная тема

>2. Что делать? Оставить всё как есть или, к примеру, изменить сканер лексем, чтобы он выделял отдельные слова и изменить грамматику? Понятно, что это потребует введения "экранирования".

Лексеммы тут не при чем, скань их как есть. Грамматика не имеет никакого отношения к лексике. На этапе лексического разбора ты выделяешь лексеммы (например lexx'ом). На этапе грамматического(синтаксического) разбора ты смотришь(с помощью yacc) удовлетворяет ли последовательность лексемм синтаксису языка, вот на этом этапе ты и решаешь свою проблему. Твой язык ожидает увидеть после команды show параметр этой команды, btw лексемма help будет определена как параметр, а не как команда.

balbes
()

мне бы было интереснее написать самому. но это зависит от конечной цели

Pi ★★★★★
()

Имхо руками писать интерестнее, тем более ничего сложного...
Я бы к тому же пересмотрел вопрос show help и сделал бы проще
help show|bla|foo|bar|etc...
Тогда у тебя команда help с необязательным параметром (типа о чем помощь показывать).. Если я правильно понял задачу - такая конструкция напрашивается сама собой.

Удачи :)

godexsoft
()

Не изобретай велосипед. Сделай входным языком какой либо из ембедаббельных скриптовых (тот же guile, или tcl), и забинди на него нужную тебе функциональность.

anonymous
()
Ответ на: комментарий от syomin

Если нужна контекстно-зависимая LR(n)/L(n)/LALR(n) в одном флаконе - юзай recursive-descendant парсер.

anonymous
()
Ответ на: комментарий от anonymous

> Насколько я понимаю, на чистом LEX лексема выделяется в независимости от контекста.

man flex на предмет start conditions

vnp
()
Ответ на: комментарий от anonymous

Иихо из пушки по воробьям. Судя по требованиям задача очень простая, проще чем парсер s-exp-ов написать.

Да и контексто-завимости при грамотном подходе тут не наблюдается.

ukez
()
Ответ на: комментарий от anonymous

>Насколько я понимаю, на чистом LEX лексема выделяется в независимости от контекста

Когда то это было так, теперь flex может работать с контекстом.

info flex

achmed
()

рекомендую сделать так: использовать ассоциативный массив, в котором будут хранится имена команд и классы, отвечающие за выполнение команд, каждая команда будет САМА обрабатывать свои параметры ( + получам расширяемость): псевдокод:

class Args { ... }

class command { ..... void exec(Args) = 0; } .... std::map<string, command *> command_map; .... cin >> name; if((it = command_map.find(name)) != command_map.end()) { (*it)->ecex(args); }else { cerr << .... }

achmed
()
Ответ на: комментарий от achmed

> рекомендую сделать так: использовать ассоциативный массив, в котором будут хранится имена команд и классы, отвечающие за выполнение команд

тогда уж не "имена команд" а значение хеша от этих имен и не std::map, а обычный массив, обращение к элементам которого происходит по хешированному индексу. Так правильнее будет

ukez
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.