Coder Social home page Coder Social logo

bercik / bio Goto Github PK

View Code? Open in Web Editor NEW
1.0 3.0 1.0 16.44 MB

Programming language, name is acronym of words: built in observer.

License: GNU General Public License v2.0

Python 2.55% Java 83.18% Shell 0.11% Vim Script 0.21% HTML 13.36% Batchfile 0.01% CSS 0.49% JavaScript 0.10%

bio's Issues

FOREACH mogące przyjmować dodatkowy parametr

Możliwość podania do funkcji FOREACH opcjonalnego 3 parametru, który następnie zostanie przekazany w strukturze obj jako pole addPar. Przykład:

def retListOfStringsStartsWith(el, obj)
   % inicjalizacja pola list obiektu obj
   IF(NOT(HAS_FIELD(obj, list)), AS_LOC(obj.list, CREATE_LIST))
   % sprawdzamy czy zaczyna się od string podanego w dodatkowym polu
   % jeżeli tak to dodajemy do listy
   IF(STARTS_WITH(TO_LOWERCASE(el.val), obj.addPar), APPEND_TO_LIST(obj.list, el.val))
   % zwracamy listę
   RET(obj.list)
end

def onSTART()
   AS_LOC(tup, CR_TUP("abc", "abba", "bcd", "def"))
   % wypisze: <["abc", "abba"]>
   PRINT_LIST(FOREACH(tup, retListOfStringsStartsWith, "ab"))
end

Dodanie struktur

Dodanie struktur, wymagać będzie modyfikacji zarówno w kodzie kompilatora jak i interpretera.

Składnia struktur wygląda następująco:
a.b.c, gdzie c to pole struktury b, a b to pole struktury a. c może być dowolnego typu, a i b są strukturami. Tworzenie struktury następuje automatycznie kiedy przypisujemy jej jakieś pole. Np. mając zmienną x, jeżeli zrobimy zmienną x.a, automatycznie x stanie się strukturą z polem a.

Co należy zmodyfikować w kompilatorze:

  • Dodać nowy token dla identyfikatorów struktur id_struct: ([id][.id]*), przerobić FiniteStateAutomata
  • Uwzględnić nowy token w gramatyce
  • Odpowiednio przerobić Parser i ProgramTreeCreator
  • Dodać nowy typ id_struct w builtin_functions.xml

To powinno wystarczyć po stronie kompilatora.

Po stronie interpretera należy:

  • Uwzględnić nowy typ struktury
  • Odpowiednio przetworzyć odwołanie do var:id_struct i id:id_struct (to znaczy wyłuskać odpowiedni element lub utworzyć odpowiednie drzewo struktur)

Możliwość ustawienia parametru jako opcjonalny w builtinFunctions

Przy parametrze możliwość ustawienia parametru optional. Nie można mieszać parametrów optional i repeat. Implementacja zakłada traktowanie parametrów optional jak repeat z tym wyjątkiem, że dopuszcza jedynie maksymalnie jeden ich cykl.

Przykład:

<function>
  <name>
    IF
  </name>
  <params>
    <param>
      all
    </param>
    <param>
      call
    </param>
    <param optional="true">
      call
    </param>
  </params>
</function>

Poprawienie informacji o błędach

Dyrektywa #INCLUDE umożliwia załączanie plików, jednak psuje informacje o miejscu wystąpienia błędu (ponieważ numery linii są liczone od zera w całym pliku po rozwinięciu już dyrektyw #INCLUDE). Należy podczas rozwijania dyrektywy #INCLUDE dodać przed jej rozwinięciem linię fs:num, która informuje, że w tym miejscu następuje start pliku, a na końcu fe:num informujące o końcu pliku. Jednocześnie stworzyć mapę zawierającą numery plików i ich ścieżki (numerowane od 1!). Lekser będąc w nowej linijce powinien sprawdzić, czy zaczyna się ona na <. Jeżeli tak to jeżeli dalszy ciąg to fs, pobrać informację o numerze pliku i wrzucić na stos. Przy każdej takiej linijce inkrementować też numer linii pliku znajdującego się nad aktualnym na stosie. Jednocześnie każda inna nowa linia powoduje inkrementację pliku aktualnie znajdującego się na stosie. Podczas wydobywania tokenów podajemy linię, znak i numer pliku aktualnie znajdującego się na stosie.

Zapobiec możliwości zawieszenia kompilatora

Poniższy kod spowoduje nieskończoną rekurencję przy próbie rozwinięcia przez kompilator domyślnych argumentów dla funkcji foo i foo2:

def foo(arg1=foo2())
   DN()
end

def foo2(arg1=foo())
   DN()
end

def onSTART()
   foo()
end

Należy albo wymagać podania wszystkich parametrów funkcji w przypadku domyślnego parametru albo wykrywać takie nieskończone rekurencje i wypisywać błąd.

Importowanie modułów

Nowa dyrektywa preprocesora #IMPORT, która umożliwia określenie dodatkowych modułów jakie zostaną zaimportowane do programu. Pewne moduły (jak basic, io, arrays) są importowane domyślnie i nie da się tego zachowania zmienić. Każdy moduł zawiera funkcję wbudowane i zdarzenia wbudowane. Niezaimportowanie modułu dodatkowego oznacza, że nie możemy w programie używać tych funkcji i zdarzeń, które w tym module się znajdują, ale jednocześnie możemy nazywać własne funkcje nazwami, które zostały użyte w tym module. Powinna wzrosnąć też nieznacznie wydajność wczytywania kodu na początku pracy interpretera.

try-catch-finally

Możliwość tworzenia bloków try-catch-finally. Składnia:

def foo()
  TRY_CATCH_FINALLY
  (
    THROW(10),
    ex,
    RETURN(20),
    PRINTLN("finally", ex)
  )
end

def onSTART()
  PRINT(foo())
end

spowoduje wypisanie:

finally
10
20

Działa to tak, że TRY_CATCH_FINALLY przyjmuje:

  • blok kodu w którym może nastąpić wywołanie funkcji THROW (TRY)
  • identyfikator zmiennej do której zostanie przypisany rzucony obiekt
  • blok obsługi błędy (CATCH)
  • blok kodu wywoływanego zawsze (FINALLY)

Jeżeli w TRY nastąpi przerwanie wykonywania (poprzez RETURN, BREAK, CONTINUE lub THROW) to wywoływana jest mini procedure obsługi kodu FINALLY. Po wykonaniu kod FINALLY następuje powrót do miejsca gdzie zostało przekazane sterowanie do bloku FINALLY, czyli w naszym powyższym przykładzie do miejsca RETURN(20). Ze względu na specyfikę problemu potrzebne są modyfikacje zarówno w kodzie kompilatora jak i interpretera.

W kompilatorze:

  • przy generowaniu kodu pośredniego funkcji na samej górze umieszczenie informacji o obsługiwanych blokach kodu, tzn.: from, to, target
    <from, to> to zakres obsługiwanego przez procedurę kodu, czyli jeżeli wyjątek wystąpi w tym bloku zostanie obsłużony przez procedurę znajdującą się w miejscu target.
  • Dodatkowo muszą zostać dodane nowe funkcje interpretera, które umożliwią powrót z procedury obsługi FINALLY do miejsca w którym została wywołana (być może uda się temu zapobiec po prostu wywołując w jakiś sposób funkcję finally).

W interpreterze:

  • Sprawdzanie czy wyjątek da się obsłużyć przez aktualnie wykonywaną funkcję, jeżeli nie to przekazanie wyżej.
  • Inicjalizacja zmiennej przechowującej rzucony obiekt.

Moduł foreach

Moduł zawierający funkcję FOREACH, umożliwiający iterowanie po każdym elemencie w tablicy, krotce lub słowniku. Definicja: FOREACH(<array, tuple, dict>, ). Funkcja zwraca ostatnią zwróconą wartość z przekazanej funkcji. Definicja przekazywanej funkcji: function(el, obj).

el to struktura posiadająca jedno lub dwa pola:

  • val - będące wartością kolejnego iterowanego obiektu
  • key - będące kluczem w przypadku iterowania po słowniku

obj to struktura nie posiadająca pól, ale przekazywana pomiędzy kolejnymi wywołaniami funkcji dla jednego wywołania FOREACH. Służy do zapisywania jakichś informacji.

Przykład:

def sumEACH(el, obj)
  IF(NOT(HAS_FIELD(obj, sum)), AS_LOC(obj.sum, 0))
  AS_LOC(obj.sum, ADD(obj.sum, el.val))
  RET(obj.sum)
end

def onSTART()
  ASSIGN_LOCAL(tup, CREATE_TUPLE(1, 2, 3, 4, 5))
  PRINT("sum:", FOREACH(tup, sumEach), "\n")
end

Możliwość aliasowania nazw funkcji

W pliku builtin_functions.xml możliwość dodania tagu nazwa_alternatywna dla funkcji, co pozwoli na użycie wielu nazw w kodzie źródłowym dla tej samej funkcji. W skompilowanym kodzie pośrednim alias zostaje zastąpiony nazwą oryginalną funkcji.

Dodanie optymalizacji

Dodanie optymalizacji dla np. przypadku kiedy instrukcja clear_stack występuje na początku funkcji użytkownika.

Zdarzenie/funkcja onUNHANDLED_ERROR

Jeżeli z funkcji onSTART zostanie zwrócony obiekt błędu, to zostaje wykonana funkcja (o ile jest taka już zdefiniowana) onUNHANDLED_ERROR. Jej definicja:

def onUNHANDLED_ERROR(err)
  % do something with error
end

Z funkcji onSTART może zostać zwrócony obiekt błędu w dwóch wypadkach: funkcja RETURN lub jmp_if_false z złym typem argumentu (nie bool).

Należy zauważyć, że jeżeli funkcja onUNHANDLED_ERROR wywoła funkcję onSTART i ta zwróci błąd, to zostanie on przekazany tym razem do funkcji onUNHANDLED_ERROR w miejscu wywołania w niej funkcji onSTART. Zwrócenie z funkcji onUNHANDLED_ERROR obiektu błędu, powoduje po prostu zakończenie działania programu. Jeżeli funkcja onUNHANDLED_ERROR nie została zdefiniowana, to zwrócenie obiektu błędu z funkcji onSTART spowoduje wypisanie tego błędu na ekran.

Możliwość dzielenia parametrów cyklicznych

W pliku .xml z definicjami funkcji możliwość dodania atrybutu split="true", który informuje kompilator o tym, aby dzielił wywołania wszystkich parametrów cyklicznych, tzn. przykładowo dla takiej definicji:

<function>
        <name>
            ASSIGN_LOCAL
        </name>
        <params split="true">
            <param>
                id
            </param>
            <param>
                all
            </param>
            <param repeat="true">
                id
            </param>
            <param repeat="true">
                all
            </param>
        </params>
</function>

i takiego wywołania funkcji:

ASSIGN_LOCAL(i, 0, j, i)

wygenerował kod, jakby wywołanie wyglądało w ten sposób:

ASSIGN_LOCAL(i, 0)
ASSIGN_LOCAL(j, i)

Możliwość odłączania callbacków od eventów w czasie ich wywołania

Przykładowy kod:

def onSTART()
  ATTACH_TO_EVENT(recursive, recursive)
  recursive(10)
end

def recursive(n)
  IF
  (
    IS_GLOBAL(n),
    ASSIGN_GLOBAL(n, SUB(GET_GLOBAL(n), 1)),
    ASSIGN_GLOBAL(n, n),
  )

  % jeżeli n jest mniejsze lub równe 0 to przerywamy rekurencję odłączając callback od eventu
  IF
  (
    LE(GET_GLOBAL(n), 0),
    DETACH_FROM_EVENT(recursive, recursive),
    DN()
  )
end

Implementacja musiałaby zmienić aktualne zachowanie, którym jest dodawanie od razu wszystkich ramej funkcji obserwatorów na stos ramek na takie w którym po każdym wyjściu z funkcji są dodawani obserwatorzy. Należałoby rozważyć wprowadzenie tego usprawnienia razem z dodaniem zmiennych statycznych w funkcjach.

Dodanie ścieżki do katalogu z standardowymi modułami i bibliotekami

W jakiś sposób przekazywać do kompilatora (np. poprzez zmienne środowiskowe (environment variable) albo właściwości systemu (system property) informację o ścieżce ze standardowymi modułami i bibliotekami bio (.biol i .biom). Następnie w momencie kiedy w dyrektywie #INCLUDE argument będzie zawarty w znakach mniejszości i większości (<>) to przeszukujemy ten katalog, a nie normalną ścieżkę. Przykład:

INCLUDE("list.biom") -> przeszukuje katalog w którym znajduje się plik źródłowy

INCLUDE("<list.biom>") -> przeszukuje katalog z standardowymi bibliotekami

Zmienne statyczne w funkcji

Możliwość utworzenia zmiennych statycznych w funkcji:

def foo()
  IF
  (
    IS_STATIC(x),
    ASSIGN_STATIC(x, ADD(GET_STATIC(x), 1)
    ASSIGN_STATIC(x, 0)
  )
end

def onSTART()
  FOR
  (
    ASSIGN_LOCAL(i, 0),
    LS(i, 10),
    foo(),
    INC(i)
  )
end

Dodać funkcję END_FOREACH

Dodać do modułu iter funkcję END_FOREACH przyjmującą jeden parametr, wartość zwracaną. Funkcja przerywa działanie funkcji FOREACH i zwraca to co przekazano jej w argumencie. Funkcja END_FOREACH musi być wywołana wewnątrz funkcji wywoływanej przez funkcję FOREACH, inaczej zostaje zwrócony błąd.

Dyrektywa #DEFINE

Dodanie dyrektywy preprocesora #DEFINE(id, val), która spowoduje zamienienie wszystkich wystąpień identyfikatora id (ale nie w nazwie funkcji) na wartość val, przy czym wartość val może być dowolna (także błędna).

Dodanie domyślnych parametrów funkcji

Składnia:
def fun_name(par1, par2, par3=10, par4=true, par5=none)

Wszystkie modyfikacje związane z tym featurem będą dokonane po stronie kompilatora.

  1. Należy dodać nowy token dla znaku równości.
  2. Należy zmodyfikować gramatykę, aby umożliwiała przy definicji parametrów funkcji po znaku równości podanie domyślnej wartości tego parametru, jeżeli wywołujący funkcję go nie poda.
  3. Należy odpowiednio zmodyfikować Parser i ProgramTreeCreator, a także klasy przechowujące funkcję i parametry.
  4. Sprawdzanie semantyki powinno uwzględniać domyślne parametry funkcji
  5. Podczas generacji kodu, jeżeli wywołujący nie podaje jakiegoś parametru należy wstawić w to miejsce jego domyślną wartość

Dodanie wyrażeń matematycznych

Podobnie jak w bashu, kod znajdujący się pomiędzy jakimiś separatorami, umożliwiający umieszczenie zwykłych wyrażeń matematycznych. Przykładowo:

def onSTART()
  ASSIGN_LOCAL(size, 10)
  ASSIGN_LOCAL(new_size, { size * 2 })
end

Wszystkie wystąpienia {wyrażenie} byłyby przed procesem kompilacji właściwej parsowane i tłumaczone przez osobny parser na odpowiednie wywołania funkcji w języku BIO. Przykładowo dla powyższego kodu:

def onSTART()
  ASSIGN_LOCAL(size, 10)
  ASSIGN_LOCAL(new_size, MUL(size, 2))
end

Wiele iteracji optymalizacyjnych

Wykonywanie optymalizacji tak długo dopóki następują jakieś zmiany. Rozważyć czy opłaca się to w stosunku czas kompilacji do optymalizacja kodu.

Zdarzenia

Implementacja możliwości wywoływania zdarzeń w interpreterze

Poprawić działanie EQ

Uniemożliwić porównywanie dwóch wartości typu error. Przekazanie jako parametru wartości typu error powinno spowodować zwrócenie błędu BAD_PARAMETER_TYPE.

Dodać możliwość wywołania funkcji FOREACH w innej

Przykładowo:

def sumEACH(el, obj)
   AS_LOC(tup, CR_TUP(1, 2, 3))
   PRINTLN(FOREACH(tup, mulEACH))

   IF(NOT(HAS_FIELD(obj, sum)), AS_LOC(obj.sum, 0))
   AS_LOC(obj.sum, ADD(obj.sum, el.val))
   RET(obj.sum)
end

def mulEACH(el, obj)
   IF(NOT(HAS_FIELD(obj, prod)), AS_LOC(obj.prod, 1))
   AS_LOC(obj.prod, MUL(obj.prod, el.val))
   RET(obj.prod)
end

def foo()
   AS_LOC(tup, CR_TUP(1, 2, 3))
   PRINTLN(FOREACH(tup, sumEACH))
end

def onSTART()
   foo()
end

Spowoduje wypisanie błędnych wyników. Należy w CallFrame kojarzyć ramkę z instancją funkcji foreach (danymi dla danej funkcji FOREACH takimi jak: nazwa, obj, iterable itd.).

Napisać testy (przepisać przykłady z examples używając zamiast PRINT, ASSERT)

Napisać moduł biotest.biom, który definiuje 4 funkcje: BT_ASSERT_EQ, BT_ASSERT_NEQ, BT_ASSERT_TRUE, BT_ASSERT_FALSE. Przepisać testy tak aby używały tych funkcji. Moduł pobiera listę funkcji i je wykonuje, jeżeli przy wywołaniu BT_ASSERT_* wystąpi błąd to wyświetla odpowiedni komunikat i podsumowanie na końcu testu. Dodatkowo napisać skrypt, który kompiluje i odpala wszystkie pliki .bio w danym katalogu i podkatalogach(?).

Dodanie modułów

Dodanie wszystkich modułów i ich dokumentacji wyszczególnionych w plikach .xml w katalogu res kompilatora.

Checklist:
arrays [x]
basic [x]
compare [x]
conversion [x]
errors [x]
floats [x]
ints [x]
io [x]
logic [x]
math [x]
observer [x]
special [x]
strings [x]
type_check [x]

Przekazywanie referencji do funkcji

def avg(@foo, n)
  ASSIGN_LOCAL(sum, 0.0)
  ASSIGN_LOCAL(count, 0)
  FOR
  (
    ASSIGN_LOCAL(i, 0),
    LS(i, n),
    CALL
    (
      ASSIGN_LOCAL(sum, ADD(sum, @foo(i))),
      INC(count)
    ),
    INC(i)
  )
  RETURN(DIV(sum, TO_FLOAT(count)))
end

def nat(n)
  RETURN(n)
end

def mul(n)
  RETURN(MUL(n, n))
end

def onSTART()
  PRINTLN(avg(@nat))
  PRINTLN(avg(@mul))  
end

biotest - narzędzie do testowania

Narzędzie umożliwiające przetestowanie programów napisanych w bio. Do działania potrzebuje dwóch katalogów: z plikami źródłowymi .bio i z plikami wynikowymi .txt. Narzędzie kompiluje programy źródłowe, uruchamia i porównuje ich wyjście z tym co znajduje się w pliku wynikowym o tej samej nazwie. Jeżeli oba teksty są zgodne to znaczy, że test przebiegł pomyślnie, inaczej wypisujemy informację o błędzie.

Nazywane parametry

Podczas wywoływania funkcji możliwość podania dla jakiego parametru jest dany argument. Przykład:

def foo(name, age):
  PRINT(ADD("your name is ", name, " and you are ", TO_STR(age), " years old"))
end

def onSTART():
  foo(age=30, name="Adam")
end

Wywoływanie funkcji poprzez stringa

Możliwość wywołania funkcji użytkownika o nazwie przechowywanej w stringu. Cała obsługa po stronie interpretera w odpowiednim module.

Definicja w pliku .xml:

<function>
  <name>
    CALL_BY_NAME
  </name>
  <params>
    <param>
      all
    </param>
    <param repeat="true">
      all
    </param>
  </params>
</function>

Następnie w interpreterze w implementacji funkcji, należy sprawdzić czy funkcja o nazwie przekazanej w pierwszym argumencie istnieje jako funkcja użytkownika i czy przekazano dla niej dobrą ilość parametrów. Następnie wywołać.

Przykład:

def pow2(arg)
  RETURN(POW(arg, 2))
end

def pow3(arg)
  RETURN(POW(arg, 3))
end

def onSTART()
  PRINTLN(CALL_BY_NAME("pow2", 2)) % 4
  PRINTLN(CALL_BY_NAME("pow3", 2)) % 8
end

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.