Jak wdrożyć aplikację do przetwarzania poleceń?

9

Chcę stworzyć prostą, sprawdzoną koncepcję aplikacji (REPL), która pobiera liczbę, a następnie przetwarza polecenia na tym numerze.

Przykład: zaczynam od 1. Następnie piszę „ add 2”, daje mi 3. Następnie piszę „ multiply 7”, daje mi 21. Następnie chcę wiedzieć, czy jest liczbą pierwszą, więc piszę „ is prime” (na bieżącym numerze - 21), daje mi to fałsz. „ is odd” dałoby mi prawdę. I tak dalej.

Teraz, w przypadku prostej aplikacji z kilkoma poleceniami, nawet proste switchbyłoby w stanie przetworzyć polecenia. Ale jeśli chcę rozszerzalności, jak powinienem wdrożyć tę funkcjonalność? Czy używam wzorca polecenia? Czy zbuduję prosty parser / interpreter dla języka? Co jeśli chcę bardziej złożone polecenia, takie jak „ multiply 5 until >200”? Jaki byłby łatwy sposób na rozszerzenie go (dodanie nowych poleceń) bez ponownej kompilacji?

Edycja: aby wyjaśnić kilka rzeczy, moim ostatecznym celem nie byłoby zrobienie czegoś podobnego do WolframAlpha, ale raczej procesor listy (liczb). Ale najpierw chcę zacząć powoli (od pojedynczych liczb).

Mam na myśli coś podobnego do tego, w jaki sposób Haskell mógłby przetwarzać listy, ale bardzo prosta wersja. Zastanawiam się, czy wystarczyłoby coś takiego jak wzorzec poleceń (lub odpowiednik), czy też muszę stworzyć nowy mini-język i parser, aby osiągnąć moje cele?

Edit2: Dzięki za wszystkie odpowiedzi, wszystkie były dla mnie bardzo pomocne, ale Emmad Kareem pomógł mi najbardziej, więc wybiorę to jako odpowiedź. Dzięki jeszcze raz!

Nini Michaels
źródło
2
Nie dostaję głosów negatywnych. Podaj swój powód, abym mógł lepiej sformułować swoje pytanie następnym razem.
Nini Michaels,
2
Naprawdę podoba mi się to pytanie. Nie możemy się doczekać, aby zobaczyć, jakie projekty sugerują ludzie. Czy szczególnie szukasz projektowania obiektowego (wspominasz wzorzec poleceń, który jest wzorcem OO)?
Bjarke Freund-Hansen
dziękuję :) tak, wolałbym OOP, ale nie mam nic przeciwko, jeśli sugerowane są inne metody!
Nini Michaels,
2
Wydaje się, że jest to implementacja Reverse Polish Notation , bardzo fajnego przedmiotu programistycznego!
Alberto De Caro
2
Prawdopodobnie spada faul w książce klauzuli z Jakich pytań mam nie pytać tutaj? w FAQ, tzn. jeśli potrafisz wyobrazić sobie całą książkę, która odpowiada na twoje pytanie, zadajesz zbyt wiele .
Mark Booth

Odpowiedzi:

5

To brzmi jak tłumacz. Wygląda na to, że martwisz się bardziej implementacją niż szczegółową funkcjonalnością (tutaj tylko zgaduję). Przedłużony projekt nie jest trywialnym zadaniem. Upewnij się, że dokładnie przestudiowałeś zakres, ponieważ wymaga to podejścia inżynieryjnego, a nie podejścia doraźnego w celu uzyskania niezawodnego produktu, a nie produktu z 1000 poprawkami, który działa tylko czasami.

Wybierz składnię i przygotuj się do jej przeanalizowania i wykonania niezbędnych kontroli składni. Ten link może ci w tym pomóc: Utwórz własny parser .

Spójrz na: ten temat, ponieważ dotyczy różnych aspektów pracy, są też dobre linki, które mogą ci pomóc (szczególnie odpowiedź RMK) .: Tworzenie tłumacza języka . Możesz zobaczyć przykład ładnie wyglądającego projektu, który jest nieco podobny w: Ultimate Programmable Scientific Calculator . Możesz znaleźć kod źródłowy i działający program dla interpretera wiersza poleceń C # tutaj Command-Line-Implementation-of-C # -Made-for-Teaching . Używanie kompilatora do wykonywania skomplikowanych zadań, takich jak parsowanie i pisanie zmiennych itp., Może być sprytnym sposobem na uniknięcie złożoności pisania tego wszystkiego samemu. Istnieje również opcja Mono, która zapewnia funkcję powłoki charp, którą możesz rzucić okiem: CsharpRepl .

Bez szans
źródło
Dzięki, twoje linki są naprawdę pomocne! Sądzę więc, że najlepszym wyborem byłby tłumacz, jeśli chcę go łatwo rozszerzyć.
Nini Michaels,
Dziękuję za opinie, myślę, że rozpoczęcie od linku do CodeProject może być dobrym pomysłem.
NoChance,
2

Jeśli nie jesteś specjalnie zainteresowany napisaniem samego parsera dla siebie, proponuję rzucić okiem na jedną z ram generatora parsera. W przypadku C masz YACC lub Bison , ale powinny istnieć inne alternatywy dla innych języków, jeśli wolisz.

Eliminuje to złożoność analizy złożonych gramatyk i pozwala skoncentrować się na zadaniu, które chcesz wykonać. Oczywiście może to być przesada w gramatyce, którą sugerujesz w pytaniu, ale ponieważ wspominasz o możliwości rozszerzenia na bardziej złożoną gramatykę, warto przynajmniej zainspirować się tymi ramami.

Harald
źródło
1
Problem z większością generatorów parserów polega na tym, że ich artefakty są statyczne i nie dają się łatwo rozszerzyć. Sądzę, że OP będzie lepiej obsługiwany z czymś bardziej podobnym do silnika reguł, w którym „reguły” (słowa kluczowe i składnia) są przechowywane w elastycznej strukturze danych i oceniane po każdym wejściu.
TMN
2

To, co opisujesz, jest bardzo zbliżone do języka stosu .

Na przykład w Factor to, co opisujesz , byłoby zrobione

1
2 +
7 *
even? not

Lub możesz zdefiniować własne słowa, a następnie użyć ich, na przykład

: add ( x y -- sum ) + ;
: multiply ( x y -- product ) * ;
: odd? ( n -- ? ) even? not ;

Dzięki tym definicjom staje się powyższy przykład

1
2 add
7 multiply
odd?

Zwykle języki stosu są łatwe do przeanalizowania, ponieważ używają pojedynczych słów, które są oddzielone spacjami. Sugeruję, abyś spojrzał na Factor - może być dokładnie taki, jak chcesz. Powinno być łatwo zdefiniować słowa, które wykonują potrzebne przetwarzanie.

EDYCJA : Jeśli naprawdę chcesz zaprojektować podobny język, sugeruję, aby i tak grać z jednym z nich. Analiza języka stosu jest trywialna - dzielisz się na puste miejsca, a naiwna implementacja przetwarzania jest łatwa: wystarczy zadbać o to, co dzieje się na stosie.

Andrea
źródło
Brzmi dość łatwo i jest dobrym miejscem do rozpoczęcia. Dzięki!
Nini Michaels,
1

Ale jeśli chcę rozszerzalności, jak powinienem wdrożyć tę funkcjonalność?

Nie powinieneś Rozszerzalność tworzy całą złożoność przy bardzo niewielkim zysku. To powiedziawszy, musisz zapewnić hak do istniejącego stanu. Sposób na sprawdzenie stanu, zmodyfikowanie stanu i zapewnienie mechanizmu zwracania innych wyników (drukowanie na ekran). Będziesz potrzebował sposobu, aby kod podstawowy odkrywał moduły, ładował je i wysyłał do nich polecenia.

Czy używam wzorca polecenia?

Możesz, ale prawdopodobnie nie jest to właściwe.

Nie zamierzasz wziąć całego wejścia i wysłać go do przetworzenia, ale zamiast tego przeanalizować dane wejściowe, wysłać do właściwego modułu obsługi i pozwolić mu wykonać swoją pracę. Polecenie nie różni się w tej komunikacji; więc nie ma wzorca poleceń.

Czy zbuduję prosty parser / interpreter dla języka?

Będziesz potrzebował czegoś, aby poradzić sobie z dzieleniem danych wejściowych na tokeny. W przypadku rozwiązania rozszerzalnego prawdopodobnie nie zrobisz nic więcej. W przypadku dobrze zdefiniowanego rozwiązania posiadanie pełnego drzewa analizy zapewni lepszą wydajność, obsługę błędów i możliwość debugowania.

ale raczej procesor listy (liczb)

Być może powinieneś zajrzeć do języka LISt Processing . Zestawienie kodu i danych powinno dobrze pasować do tego, co opisujesz.

Telastyn
źródło
Dziękujemy za sugestie. Jeśli chodzi o LISP, jestem z nim zaznajomiony, a jeszcze bardziej z Haskellem, który zainspirował mnie tym pomysłem. Jednak, chociaż mogę trochę wymyślać koło, chcę ubrudzić sobie ręce przetwarzaniem poleceń i ich interpretacją. Ma więc również cel edukacyjny poza faktycznym „przetwarzaniem listy” :)
Nini Michaels,
@NiniMichaels na pewno, ale jeśli chodzi o projekt rozszerzalności, użycie organizacji lisp / łańcuchów kodu / danych nie jest złą opcją.
Telastyn