명령 인터프리터 / 파서 작성 방법


22

문제 : 문자열 형태로 명령을 실행하십시오.

  • 명령 예 :

    /user/files/ list all; 다음과 같습니다. /user/files/ ls -la;

  • 다른 것:

    post tw fb "HOW DO YOU STOP THE TICKLE MONSTER?;"

다음과 같습니다. post -tf "HOW DO YOU STOP THE TICKLE MONSTER?;"

현재 솔루션 :

tokenize string(string, array);

switch(first item in array) {
    case "command":
        if ( argument1 > stuff) {
           // do the actual work;
        }
}

이 솔루션에서 볼 수있는 문제는 다음과 같습니다.

  • 각 케이스 안에 중첩 된 ifs-else 이외의 오류 검사가 없습니다. 대본은 매우 커지고 관리하기가 어렵습니다.
  • 명령과 응답은 하드 코딩됩니다.
  • 플래그가 올바른지 또는 매개 변수가 없는지 알 수있는 방법이 없습니다.
  • "$ command를 실행하고 싶을 수도있다"는 지적 부족.

그리고 내가 해결할 수없는 마지막 것은 다른 인코딩의 동의어입니다. 예 :

case command:
case command_in_hebrew:
    do stuff;
break;

마지막 것은 사소한 것일 수도 있지만, 내가보고 싶은 것은 이런 종류의 프로그램의 확실한 자금입니다.

나는 현재 이것을 PHP로 프로그래밍하고 있지만 PERL에서 그것을 할 수 있습니다.


나는 이것이 PHP와 특별히 어떻게 관련되는지 전혀 알지 못합니다. SO 및 SE의이 인터프리터 / 컴파일러 주제에는 이미 많은 스레드가 있습니다.
Raffael

3
아무도 getopt를 언급하지 않았습니까?
Anton Barkovsky

@AntonBarkovsky : 했어요. 내 링크를 참조하십시오. Ubermensch와 같은 답변은 OP 가하려는 일에 대해 너무 복잡합니다.
quentin-starin

1
또한 RegExp를 사용하여 간단한 접근 방식을 인용했습니다. 답변도 업데이트되었습니다
Ubermensch

특정 progr을 언급하지 않았습니다. 언어. "c"태그, "ruby"태그, "php"태그를 추가 할 수 있습니다. 아마도 오픈 소스 라이브러리, 표준 라이브러리 또는 "일반적으로 아직 표준 라이브러리가 아닌 일반 라이브러리"가있을 수 있습니다. 당신의 progr. 언어.
umlcat

답변:


14

솔직히 인정하자면, 파서 작성은 지루한 작업이며 컴파일러 기술에 가깝지만 하나를 작성하는 것은 좋은 모험이 될 것입니다. 파서는 통역사와 함께 제공됩니다. 그래서 둘 다 만들어야합니다.

파서 및 통역사에 대한 빠른 소개

이것은 너무 기술적이지 않습니다. 그래서 전문가들은 걱정하지 않습니다.

일부 입력을 터미널에 공급하면 터미널이 입력을 여러 장치로 분할합니다. 입력을 표현이라고하고 여러 단위를 토큰이라고합니다. 이 토큰은 연산자 또는 기호 일 수 있습니다. 따라서 계산기에 4 + 5 를 입력하면 이 표현식은 3,4, +, 5 토큰으로 나뉩니다. 더하기 기호는 4 및 5 기호 인 동안 연산자로 간주됩니다. 이것은 연산자에 대한 정의를 포함하는 프로그램 (통역사로 간주)으로 전달됩니다. 정의 (이 경우 add)에 따라 두 기호를 추가하고 결과를 터미널에 반환합니다. 모든 컴파일러는이 기술을 기반으로합니다. 식을 여러 개의 토큰으로 나누는 프로그램을 렉서라고하며 추가 처리 및 실행을 위해 이러한 토큰을 태그로 변환하는 프로그램을 파서라고합니다.

Lex 및 Yacc 는 C 에서 BNF 문법을 기반으로하는 어휘 분석기 및 구문 분석기를 작성하기위한 표준 양식이며 권장되는 옵션입니다. 대부분의 파서는 Lex와 Yacc의 복제본입니다.

파서 / 분석가를 구축하는 단계

  1. 토큰을 기호, 연산자 및 키워드로 분류하십시오 (키워드는 연산자 임)
  2. BNF 양식을 사용하여 문법 작성
  3. 작업을위한 파서 함수 작성
  4. 프로그램으로 실행 컴파일

따라서 위의 경우 추가 토큰은 렉서에서 더하기 기호로 수행 할 작업에 대한 정의가있는 숫자 및 더하기 기호입니다.

노트와 팁

  • 왼쪽에서 오른쪽으로 LALR 을 평가하는 파서 기술을 선택하십시오.
  • 느낌을 얻으려면 컴파일러 에 대한이 용 책을 읽으십시오 . 나는 개인적으로 책을 끝내지 않았다
  • 링크 는 Python에서 Lex와 Yacc에 대한 초고속 통찰력을 제공합니다.

간단한 접근법

기능이 제한된 간단한 구문 분석 메커니즘이 필요한 경우 요구 사항을 정규식으로 바꾸고 전체 기능을 만드십시오. 예를 들어, 4 개의 산술 함수에 대한 간단한 구문 분석기를 가정하십시오. 따라서 연산자를 먼저 호출 한 다음 스타일의 함수 목록 (lisp와 유사) (+ 4 5)또는(add [4,5]) 다음 운영자와시 운영되는 기호의 목록을 얻을 수있는 간단한 정규식을 사용할 수 있습니다.

이 방법으로 가장 일반적인 경우를 쉽게 해결할 수 있습니다. 단점은 명확한 구문으로 많은 중첩 식을 가질 수 없으며 쉽게 고차 함수를 가질 수 없다는 것입니다.


2
이것은 가장 어려운 방법 중 하나입니다. 어휘 분석 및 구문 분석 패스 등 분리-매우 복잡하지만 고풍스러운 언어에 대해 고성능 구문 분석기를 구현하는 데 유용합니다. 현대 세계에서 lexerless 파싱은 가장 간단한 기본 옵션입니다. Yacc와 같은 전용 전 처리기보다 구문 분석 조합기 또는 eDSL을 사용하는 것이 더 쉽습니다.
SK-logic

SK-logic과 동의했지만 일반적인 자세한 답변이 필요하기 때문에 Lex와 Yacc 및 일부 파서 기본 사항을 제안했습니다. Anton이 제안한 getopts도 더 간단한 옵션입니다.
Ubermensch

그것이 내가 말한 것입니다-lex와 yacc는 구문 분석의 가장 어려운 방법 중 하나이며 충분히 일반적이지 않습니다. Lexerless 구문 분석 (예 : packrat 또는 간단한 Parsec 유사)은 일반적인 경우에 훨씬 간단합니다. 그리고 Dragon 책은 더 이상 구문 분석에 대한 유용한 소개가 아닙니다. 너무 오래되었습니다.
SK-logic

@ SK-logic 더 나은 책을 추천 해 줄 수 있습니까? 파싱을 이해하려고하는 사람 (적어도 나의 인식으로는)에 대한 모든 기본 사항을 다루는 것 같습니다. lex와 yacc에 관해서는 어렵지만 널리 사용되며 많은 프로그래밍 언어가 구현을 제공합니다.
Ubermensch

1
@ alfa64 : 물론 우리는 당신이 실제로이 대답을 기반으로 솔루션을 코딩 할 때 다음 알려하는
쿠엔틴-쳐다

7

첫째, 문법이나 인수를 지정하는 방법에 관해서는 자신의 것을 발명하지 마십시오. GNU 스타일의 표준은 이미 매우 인기가 잘 알려져있다.

둘째, 허용되는 표준을 사용하고 있으므로 바퀴를 재발 명하지 마십시오. 기존 라이브러리를 사용하십시오. GNU 스타일 인수를 사용하는 경우 이미 선택한 언어로 된 성숙한 라이브러리가 거의 있습니다. 예를 들어 , c # , php , c 입니다.

좋은 옵션 파싱 라이브러리는 사용 가능한 옵션에 대한 도움말을 인쇄합니다.

12/27 수정

당신이 이것을보다 복잡하게 만드는 것처럼 보입니다.

명령 행을 보면 매우 간단합니다. 옵션과 그 옵션에 대한 인수 일뿐입니다. 복잡한 문제는 거의 없습니다. 옵션은 별명을 가질 수 있습니다. 인수는 인수 목록이 될 수 있습니다.

귀하의 질문에 대한 한 가지 문제는 처리하려는 명령 줄 유형에 대한 규칙을 실제로 지정하지 않았다는 것입니다. 나는 GNU 표준을 제안했으며, 당신의 예제는 그것에 가깝습니다 (그러나 첫 번째 항목으로 경로를 가진 첫 번째 예제를 실제로 이해하지 못합니까?).

우리가 GNU를 말하는 경우, 단일 옵션은 별칭으로 긴 형식과 짧은 형식 (단일 문자) 만 가질 수 있습니다. 공백이 포함 된 모든 인수는 따옴표로 묶어야합니다. 여러 개의 짧은 양식 옵션을 연결할 수 있습니다. 짧은 양식 옵션은 단일 대시로, 긴 양식은 두 개의 대시로 진행해야합니다. 마지막으로 연결된 짧은 양식 옵션 만 인수를 가질 수 있습니다.

매우 간단합니다. 매우 일반적입니다. 또한 5 번 이상 찾을 수있는 모든 언어로 구현되었습니다.

쓰지 마십시오. 이미 작성된 것을 사용하십시오.

표준 명령 줄 인수 이외의 것을 염두에 두지 않는 한, 기존에 테스트를 거친 많은 라이브러리 중 하나를 사용하십시오.

합병증은 무엇입니까?


3
항상 오픈 소스 커뮤니티를 활용하십시오.
스펜서 Rathbun

getoptionkit을 사용해 보셨습니까?
alfa64

아니요, 저는 몇 년 동안 PHP에서 일하지 않았습니다. 다른 PHP 라이브러리도있을 수 있습니다. 내가 링크 한 c # 명령 줄 파서 라이브러리를 사용했습니다.
quentin-starin

4

http://qntm.org/loco 와 같은 것을 이미 시도 했습니까 ? 이 접근 방식은 손으로 쓴 애드혹보다 훨씬 깔끔하지만 레몬과 같은 독립형 코드 생성 도구는 필요하지 않습니다.

편집 : 복잡한 구문으로 명령 줄을 처리하는 일반적인 방법은 인수를 공백으로 구분 된 단일 문자열로 다시 결합한 다음 도메인 특정 언어의 표현 인 것처럼 올바르게 구문 분석하는 것입니다.


+1 멋진 링크, github 또는 다른 곳에서 사용할 수 있는지 궁금합니다. 그리고 사용 조건은 어떻습니까?
hakre

1

당신은 당신의 문법에 대한 많은 세부 사항을 제시하지 않았으며 몇 가지 예를 들었습니다. 내가 볼 수있는 것은 약간의 문자열, 공백 및 (아마도 귀하의 예가 귀하의 질문에 무관심합니다) 큰 따옴표로 묶인 문자열과 하나의 ";"가 있다는 것입니다 끝에.

PHP 구문과 비슷할 수 있습니다. 그렇다면 PHP에 파서가 제공되므로 재사용하고보다 구체적으로 확인할 수 있습니다. 마지막으로 토큰을 처리해야하지만 이것은 왼쪽에서 오른쪽으로 보이므로 실제로 모든 토큰에 대한 반복입니다.

PHP 토큰 파서 ( token_get_all) 를 재사용하는 몇 가지 예가 다음 질문에 대한 답변에 제공됩니다.

두 예제에는 간단한 파서도 포함되어 있으며 아마도 시나리오에 적합한 것과 유사한 것이 있습니다.


그래, 나는 문법을 서두르고 지금 추가 할 것이다.
alfa64

1

당신의 요구가 단순하고 둘 다 시간이 있고 그것에 관심이 있다면, 나는 여기서 결정에 반대하고 자신의 파서를 작성하는 것을 부끄러워하지 말라고 말할 것입니다. 다른 것도 없다면 좋은 학습 경험입니다. 중첩 된 함수 호출, 배열 등보다 복잡한 요구 사항이있는 경우 시간이 오래 걸릴 수 있습니다. 자신의 롤링에 대한 가장 큰 장점 중 하나는 시스템과의 통합에 문제가 없다는 것입니다. 물론 단점은 모든 실수가 당신의 잘못이라는 것입니다.

그러나 토큰에 대한 작업은 하드 코딩 된 명령을 사용하지 마십시오. 그런 다음 비슷한 소리 명령과 관련된 문제가 사라집니다.

모든 사람은 항상 드래곤 북을 추천하지만, 나는 항상 Ronald Mak의 "쓰기 컴파일러와 통역사" 가 더 나은 소개가된다는 것을 알게되었습니다 .


0

나는 그런 식으로 작동하는 프로그램을 작성했습니다. 하나는 유사한 명령 구문을 가진 IRC 봇이었습니다. 이 큰 파일 큰 스위치 문입니다. 작동하지만 빠르게 작동하지만 유지 관리가 다소 어렵습니다.

OOP 스핀이 더 많은 다른 옵션은 이벤트 핸들러를 사용하는 것입니다. 명령과 전용 기능을 사용하여 키-값 배열을 만듭니다. 명령이 주어지면 배열에 주어진 키가 있는지 확인합니다. 그렇다면 함수를 호출하십시오. 이것은 새로운 코드에 대한 나의 추천이 될 것입니다.


나는 당신의 코드를 읽었으며 그것은 내 코드와 정확히 같은 체계이지만, 내가 언급했듯이 다른 사람들이 사용하기를 원한다면 오류 검사와 물건을 추가해야합니다.
alfa64

1
@ alfa64 의견 대신 질문에 대한 설명을 추가하십시오. 정확히 무엇을 요구하는지는 확실하지 않지만, 실제로 구체적인 것을 찾고 있다는 것은 다소 분명합니다. 그렇다면 정확히 무엇인지 알려주십시오 . 나는에서 가서 아주 쉽게 생각하지 않습니다 I think my implementation is very crude and faultybut as i stated, if you want other people to use, you need to add error checking and stuff정확히 그것에 대해 원유의 어떤 결함, 그것은 당신이 더 나은 답변을 얻을 도움이 될 ... 알려주세요.
yannis

확실히, 나는 질문을 재 작업 할 것이다
alfa64

0

컴파일러 나 인터프리터를 직접 구현하는 대신 도구를 사용하는 것이 좋습니다. Irony는 C #을 사용하여 대상 언어 문법 (명령 줄의 문법)을 표현합니다. CodePlex에 대한 설명은 "Irony는 .NET 플랫폼에서 언어를 구현하기위한 개발 키트입니다."라고 말합니다.

CodePlex : Irony-.NET 언어 구현 키트 의 Irony 공식 홈페이지를 참조하십시오 .


PHP와 함께 어떻게 사용 하시겠습니까?
SK-logic

질문에 PHP 태그 또는 PHP에 대한 참조가 없습니다.
Olivier Jacot-Descombes

필자는 원래 PHP에 관한 것이었지만 이제는 다시 작성되었습니다.
SK-logic

0

내 조언은 문제를 해결하는 라이브러리에 대한 Google입니다.

최근 NodeJS를 많이 사용하고 있으며 Optimist 는 명령 줄 처리에 사용합니다. 본인이 선택한 언어로 사용할 수있는 언어를 검색하는 것이 좋습니다. 그렇지 않다면 .. 하나를 작성하고 소스를 공개하십시오.


0

왜 요구 사항을 약간 단순화하지 않습니까?

전체 파서, 너무 복잡하고 귀하의 경우에 필요하지 않은 구문 분석기를 사용하지 마십시오.

루프를 만들고 "프롬프트"를 나타내는 메시지를 작성하면 현재 경로가 될 수 있습니다.

문자열을 기다렸다가 "파싱"하고 문자열의 내용에 따라 무언가를합니다.

문자열은 줄을 예상하는 것처럼 "구문 분석"할 수 있으며, 공백은 구분 기호 ( "토큰 라이저")이며 나머지 문자는 그룹화됩니다.

예.

프로그램은 출력하고 같은 줄에 유지됩니다. / user / files / 사용자는 같은 줄에 모든 것을 씁니다.

프로그램은 다음과 같은 목록, 컬렉션 또는 배열을 생성합니다

list

all;

또는 ";"인 경우 공백과 같은 구분자로 간주됩니다.

/user/files/

list

all

여러분의 프로그램은 유닉스 스타일의 "파이프"없이, 윈도즈 스타일의 리디렉션이 아닌 하나의 단일 명령을 기대함으로써 시작할 수 있습니다.

프로그램은 명령어 사전을 만들 수 있으며, 각 명령어마다 매개 변수 목록이있을 수 있습니다.

명령 디자인 패턴은 다음과 같은 경우에 적용됩니다.

http://en.wikipedia.org/wiki/Command_pattern

이 "일반 c"의사 코드는 테스트 또는 완료되지 않았으며 수행 방법에 대한 아이디어입니다.

또한 객체 지향적으로 만들거나 프로그래밍 언어로 원하는대로 만들 수 있습니다.

예:


// "global function" pointer type declaration
typedef
  void (*ActionProc) ();

struct Command
{
  char[512] Identifier;
  ActionProc Action; 
};

// global var declarations

list<char*> CommandList = new list<char*>();
list<char*> Tokens = new list<char*>();

void Action_ListDirectory()
{
  // code to list directory
} // Action_ListDirectory()

void Action_ChangeDirectory()
{
  // code to change directory
} // Action_ChangeDirectory()

void Action_CreateDirectory()
{
  // code to create new directory
} // Action_CreateDirectory()

void PrepareCommandList()
{
  CommandList->Add("ls", &Action_ListDirectory);
  CommandList->Add("cd", &Action_ChangeDirectory);
  CommandList->Add("mkdir", &Action_CreateDirectory);

  // register more commands
} // void PrepareCommandList()

void interpret(char* args, int *ArgIndex)
{
  char* Separator = " ";
  Tokens = YourSeparateInTokensFunction(args, Separator);

  // "LocateCommand" may be case sensitive
  int AIndex = LocateCommand(CommandList, args[ArgIndex]);
  if (AIndex >= 0)
  {
    // the command

    move to the next parameter
    *ArgIndex = (*ArgIndex + 1);

    // obtain already registered command
    Command = CommandList[AIndex];

    // execute action
    Command.Action();
  }
  else
  {
    puts("some kind of command not found error, or, error syntax");
  }  
} // void interpret()

void main(...)
{
  bool CanContinue = false;
  char* Prompt = "c\:>";

  char Buffer[512];

  // which command line parameter string is been processed
  int ArgsIndex = 0;

  PrepareCommandList();

  do
  {
    // display "prompt"
    puts(Prompt);
    // wait for user input
      fgets(Buffer, sizeof(Buffer), stdin);

    interpret(buffer, &ArgsIndex);

  } while (CanContinue);

} // void main()

프로그래밍 언어에 대해서는 언급하지 않았습니다. 모든 프로그래밍 언어를 언급 할 수도 있지만 "XYZ"가 바람직합니다.


0

당신은 당신보다 몇 가지 작업이 있습니다.

요구 사항을 살펴보면 ...

  • 명령을 구문 분석해야합니다. 상당히 쉬운 작업입니다
  • 확장 가능한 명령 언어가 필요합니다.
  • 오류 확인 및 제안이 필요합니다.

확장 가능한 명령 언어는 DSL이 필요함을 나타냅니다. 확장 기능이 간단한 경우 직접 롤링하지 않고 JSON을 사용하는 것이 좋습니다. 그것들이 복잡하면 s- 표현식 구문이 좋습니다.

오류 검사는 시스템이 가능한 명령에 대해서도 알고 있음을 의미합니다. 그것은 사후 명령 시스템의 일부일 것입니다.

경우 내가 처음부터 이러한 시스템을 구현했다, 나는 벗겨 다운 리더 커먼 리스프를 사용합니다. 각 명령 토큰은 s-expression RC 파일에 지정된 심볼에 매핑됩니다. 토큰 화 후에는 제한된 컨텍스트에서 평가 / 확장되어 오류를 포착하며 인식 가능한 오류 패턴은 제안을 리턴합니다. 그 후에 실제 명령이 OS로 전달됩니다.


0

함수형 프로그래밍 에는 멋진 기능이 있습니다 관심을 가질만한 이 있습니다.

패턴 매칭 이라고합니다 합니다.

다음은 ScalaF # 에서 패턴 일치에 대한 두 가지 링크입니다 .

나는 당신이 switch 구조 하는 것이 약간 지루 , 특히 Scala에서 컴파일러를 구현하는 동안 patern matching을 사용하는 것을 즐겼습니다.

특히 람다 미적분학 을 살펴 보는 것이 좋습니다. Scala 웹 사이트 예제 .

제 생각에는 가장 현명한 방법이지만 PHP를 엄격하게 고수 해야하는 경우 "old-school"이 붙어 switch있습니다.


0

Apache CLI를 확인하십시오. 전체 목적은 원하는 작업을 정확하게 수행하는 것 같습니다. 사용할 수 없어도 아키텍처를 확인하고 복사 할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.