bercik / bio Goto Github PK
View Code? Open in Web Editor NEWProgramming language, name is acronym of words: built in observer.
License: GNU General Public License v2.0
Programming language, name is acronym of words: built in observer.
License: GNU General Public License v2.0
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, 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:
To powinno wystarczyć po stronie kompilatora.
Po stronie interpretera należy:
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>
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.
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.
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.
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:
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:
W interpreterze:
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:
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
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 dla np. przypadku kiedy instrukcja clear_stack występuje na początku funkcji użytkownika.
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.
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)
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.
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:
Domyślnie zwracać ma none
W plikach źródłowych możliwość napisania komentarzy o odpowiedniej składni, które potem narzędzie biodoc zamienia na plik .html z dokumentacją.
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
W funkcji IF, zamienić definicję na: IF ( var, call_if_true, < call_if_false> )
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.
Zamiana funkcji CALL2 na funkcję CALL, przyjmującą jedną lub więcej funkcji do wywołania. Potrzebne zmiany w BuiltinFunctionsParser, SemanticChecker i CodeGenerator.
Jeżeli spróbujemy stworzyć ujemną liczbę typu float, zaczynającą się od zera to dostaniemy błąd Lexera.
Przykładowo: -0.5
Błąd: Unexpected character 0 in token "-"
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).
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.
Sortowanie po nazwie, czasie i średnim czasie (być może jako opcja kompilatora/interpretera)
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
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.
Implementacja możliwości wywoływania zdarzeń w interpreterze
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.
W interpreterze zamiana wszystkich new PerformCallLoc(...) (przykładowo) na wywołanie funkcji obiektu PerformCallLoc.
Jeżeli jakaś funkcja zwraca w wyniku tuplę to zmienić na zwracanie array.
Przepisać testy używając zamiast PRINT modułu biotest.biom.
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ć 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 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]
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
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.
W funkcji ATTACH_TO_EVENT dodać sprawdzanie, czy ilość parametrów w obu funkcjach jest taka sama.
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
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
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.