Last active
December 18, 2015 21:39
-
-
Save ozkriff/5848970 to your computer and use it in GitHub Desktop.
Очень-преочень черновой набросок примера программы на Misery и кое-какая документация.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# MISERY LANGUAGE | |
# =============== | |
# | |
# Github | |
# ------ | |
# | |
# github.com/ozkriff/misery | |
# | |
# | |
# Форматирование и пробелы | |
# ------------------------ | |
# | |
# Отступ - 2 пробела. | |
# | |
# Если "(" и ")" помещаются на одной строчке, то пробел вокруг | |
# них не ставится. Иначе, собственно, ставится. | |
# 'if', 'else', 'for', '->' - всегда окружены пробелами. | |
# | |
# | |
# Инъекция зависимостей | |
# --------------------- | |
# | |
# Использовать и любить, да. | |
# | |
# | |
# Модульные тесты | |
# --------------- | |
# | |
# Проверять покрытие кода модульными проверками. | |
# Каждый путь выполнения в модуле должен быть выполнен при запуске | |
# соответствующего test_*.py файла! | |
# Покрытие функционаьных проверок не учитывается. | |
# | |
# | |
# Функциональные тесты | |
# -------------------- | |
# | |
# Сложные тесты, использующие сразу несколько модулей, надо вынести | |
# в отдельный блок функциональных тестов. | |
# | |
# Пытаться скомпилировать их, слинковать, запустить и проверив вывод. | |
# | |
# Возможно, стоит вынести их из исходников в специальные файлы. | |
# | |
# | |
# Функции | |
# ------- | |
# | |
# Изначально я хотел использовать синтаксис "funcName := func(){}", | |
# но это слобо согласуется с той же перегрузкой функций, т.к. нельзя создать | |
# безымянную перегруженную функцию. | |
# | |
# Реализовать перегрузку функций и методов. | |
# | |
# Функция может иметь параметры со значением по-умолчанию: | |
# | |
# killHim := func (victim String = "Bill") {...} | |
# | |
# Методы \ Прикрепленные функции: | |
# | |
# Ключевое слово "this" для обращения к полям прикрепленного объекта | |
# и "this*" для мутирующих методов. | |
# | |
# Аналог питонячего yield`а. Почти как return. | |
# | |
# | |
# Импорт | |
# ------ | |
# | |
# Импорт как в питоне: "import std.Vector", тогда можно писать | |
# не "std.Vector", а просто "Vector". | |
# Хотя, надо по-лучше подумать над "qualified import". | |
# | |
# Не разрешать циклические зависимости между модулями. | |
# | |
# Возможно, использовать два слова - 'use' и 'import'. | |
# | |
# | |
# Инкапсуляция | |
# ------------ | |
# | |
# Если имя чего-то начинается с "_", то оно недоступно | |
# извне родительской сущности. | |
# | |
# | |
# Иммутабельность | |
# --------------- | |
# | |
# Если последний символ в имени переменной "*" - то это изменяемая переменная. | |
# иначе - константная переменная (значение задается при конструировании и | |
# потом не меняется). | |
# | |
# "*" в конце имени метода делает его мутирующим, иначе он не сможет | |
# изменять состояние объекта. | |
# | |
# "*" в конце параметра функции делает его изменяемым. | |
# При передаче изменяемых аргументов должен ставиться некий значек: | |
# | |
# inc(+intVar) | |
# | |
# | |
# Switch/case утверждение | |
# ----------------------- | |
# | |
# Каноничный switch/case: | |
# | |
# switch [optionalExpression] ( | |
# case [expression] ( | |
# # ... | |
# ) | |
# case [expression] ( | |
# # ... | |
# fallthrough | |
# ) | |
# case ( | |
# [expression1] | |
# [expression2] | |
# ) ( | |
# # ... | |
# ) | |
# else ( | |
# # ... | |
# ) | |
# ) | |
# | |
# и его сокращенный вариант "if": | |
# | |
# if [expression] ( | |
# # ... | |
# ) | |
# | |
# и "if\else": | |
# | |
# if [expression] ( | |
# # ... | |
# ) else ( | |
# # ... | |
# ) | |
# | |
# Наличествует "fallthrough" statement, позволяющий "провалиться" | |
# в следующий 'case' блок. | |
# | |
# | |
# Обработка ошибок | |
# ---------------- | |
# | |
# Да, подумай на тем, что бы сделать handle блок ближке к try-catch | |
# конструкции, а не "прицепом" к блокам. Или надо вносить handle секцию | |
# в синтаксис всех блоков. | |
# | |
# Если есть функция, которая возвращает наследника std.Error и вызывающий | |
# явным образом не проверяет его, то его тип обязан появиться в секции | |
# 'handle' сразу после текущего statement block, иначе будет ошибка сборки. | |
# | |
# Если, после вызова функции, это самое проигнорированное значение | |
# не является nil`ом, то управление сразу передается в нужную case-секцию. | |
# | |
# Еще можно потребовать выставлять значок "@" перед вызовом функции, | |
# с игнорированием значения: | |
# | |
# ''' function_call : '?' expression '(' expression_list ')' ''' | |
# | |
# Если handle лок содержит только одну case ветку, то его можно записать | |
# в сокращенном виде: "() handle (error Error) (...)", без 'case'. | |
# | |
# Таким образом, даже конструкторы могут возвращать ошибки и можно спокойно | |
# делать вложенные вызовы функций, возвращающих ошибки. Круто. | |
# | |
# Handle-блок может быть сразу после любой (?) закрывающей круглой скобки: | |
# - вызов функции | |
# - конструирование объекта (вызов конструктора) | |
# - блок statement`ов | |
# | |
# А как же if`ы (у ниж же есть ветки else) ? | |
# Думаю, handle блок может быть в конце if\else: | |
# if ... ( ... ) else ( ... ) handle (...) | |
# | |
# file := File ( | |
# name = "test.txt" | |
# mode = File.readOnly | |
# ) handle (error File.OpenError) ( | |
# handleFileOpenError(error) | |
# ) | |
# | |
# | |
# Объявление переменных | |
# --------------------- | |
# | |
# a := Integer() # без инициализации? | |
# a := Integer(0) | |
# a := 0 # вывод типа из литерала | |
# parser := Parser(Lexer) | |
# | |
# Из руководства к GoLang: | |
# | |
# Within a composite literal of array, slice, or map type T, elements | |
# that are themselves composite literals may elide the respective | |
# literal type if it is identical to the element type of T. | |
# Similarly, elements that are addresses of composite literals | |
# may elide the &T when the element type is *T. | |
# | |
# | |
# Scope Bound Resource Management | |
# ------------------------------- | |
# | |
# Реализовать Scope Bound Resource Management (RAII). | |
# | |
# | |
# Semantic diff | |
# ------------- | |
# | |
# А еще я хочу свой нормальный diff, генерирующий читаемые патчи. | |
# | |
# | |
# Introspection | |
# ------------- | |
# | |
# Интроспекция, reflection, очень. | |
# | |
# | |
# Список параметров переменной длинны для шаблонов | |
# ------------------------------------------------ | |
# | |
# Что бы реализация для 2х-мерного вектора не отличалась от 3х-мерного. | |
# | |
# | |
# datatype.py | |
# ----------- | |
# | |
# Каждое выражение должно иметь поле "тип". | |
# | |
# | |
# separator ::= ',' OR '\n' - как такая идея? | |
# | |
# Давать временным переменным в генерируемом коде более понятные имена, | |
# если есть возможность. Наприме, основываться на имени функции. | |
# | |
# http://c2.com/cgi/wiki?GoodSyntax | |
# | |
# Конструктор - просто инициализатор. | |
# | |
# import unittest | |
# tests = unittest.defaultTestLoader.discover('.') | |
# unittest.runner.TextTestRunner().run(tests) | |
# | |
# | |
# Выделение памяти в куче | |
# ----------------------- | |
# | |
# parser := alloc<Parser>(alloc<Lexer>()) | |
# | |
# | |
# camelCase | |
# --------- | |
# | |
# English has words like TCP, DNA and WTF. Should a TCP socket class | |
# be called TCPSocket or TCPsocket? What about a TCPIPSocket? | |
# What if we need a tcpOpen method - should we call it TCPOpen, | |
# like a class, to preserve the natural case of an acronym, | |
# or should it be TCPopen, so that the lowercase "o" conveys the fact | |
# that it's a function?" | |
# | |
# В этом, гм, языке вот так: tcpOpen - func, TcpIpSocket - class | |
# | |
# | |
# Операторы (утверждения) для создания-изменения перемнных | |
# -------------------------------------------------------- | |
# | |
# - '=' - присвоить значение выражения справа от оператора куску памяти, | |
# указанному в переменной слева | |
# - '<=' - изменить адрес памяти, куда ссылается переменная | |
# - ':=' - создать переменную (указатель на кусок памяти), затем то же, | |
# что и '<=' | |
# - '::=' - создать переменную и выделить для нее кусок памяти на стеке, | |
# затем то же, что и '=' | |
# | |
# | |
# Примеры | |
# ------- | |
# | |
# - создание переменной: | |
# | |
# - misery: | |
# | |
# # создаем изменяемую ('*' на конце имени) переменную | |
# # целого типа со значением 1 ... | |
# a* := Int(1) | |
# | |
# # затем присваиваем ей значение 2 ... | |
# a* = 2 | |
# | |
# - misery, аналогично, но через оператор '::=': | |
# | |
# a* ::= 1 | |
# a* = 2 | |
# | |
# - ansi_c: | |
# | |
# /* создаем переменную - просто указатель на Int ... */ | |
# Int* a; | |
# | |
# /* выделяем память на стеке для объекта Int(1) ... */ | |
# Int tmp_0; | |
# | |
# /* вызываем конструктор Int(1) - в данном случае | |
# просто присваиваем значение ... | |
# */ | |
# tmp_0 = 1; | |
# | |
# /* связываем имя 'a' и участок памяти, созданный | |
# выражением 'Int(1)' ... | |
# */ | |
# a = &tmp_0; | |
# | |
# /* а теперь уже просто меняем значение куска памяти, на который | |
# ссылается переменная ... | |
# */ | |
# *a = 2 | |
# | |
# - чуть более сложный пример: | |
# | |
# - misery: | |
# a ::= plus(1, 2) | |
# | |
# - ansi_c: | |
# /* Где-то в отдельной секции ... */ | |
# Int const_0 = 1; | |
# Int const_0 = 2; | |
# | |
# /* встроенная функция, реализована в стандартной библиотеке */ | |
# void plus_Int(Int** __result, Int* a, Int* b) { | |
# **_result = *a + *b; | |
# } | |
# | |
# /* Оператор '::=' создает переменную и сразу же выделяет | |
# для нее память на стеке ... | |
# */ | |
# Int tmp0; | |
# Int* a = &tmp0; | |
# plus_Int(&a, &const_0, &const_1); | |
# | |
# | |
# Если имя переменной кончается на '*', то она изменяема. | |
# При передаче изменяемого параметра в функцию, перед ним | |
# надо поставить '+'. | |
# | |
# | |
# M, R prefixes | |
# ------------- | |
# | |
# MyClass := class { | |
# # поле же должно быть инциализировано, так? | |
# next Opt<R:MyClass> | |
# data Int32 | |
# } | |
# | |
# start := func { | |
# m := MyClass() # ERROR: 'next' is uninitialized | |
# m2 := MyClass ( | |
# data = 32 | |
# next = Opt.None | |
# ) | |
# m3 := MyClass ( | |
# data = 1 | |
# next = MyClass ( | |
# data = 2 | |
# next = MyClass ( | |
# data = 3 | |
# next = Opt.None | |
# ) | |
# ) | |
# ) | |
# } | |
# | |
# =============================================================================== | |
import ( | |
std ( | |
Integer | |
Float | |
Vector | |
String | |
printLine* | |
) | |
date.Date | |
) | |
func update ( | |
gameObjects Vector<GameObject> | |
time Integer32 | |
) ( | |
do each(gameObjects) |gameObject| ( | |
gameObject.update(time) | |
) | |
) | |
# plain c-struct | |
# | |
class Book ( | |
store ( | |
name String | |
authors Vector<String> | |
publisherName String | |
publishingDate Date | |
) | |
attach ( | |
enum Status <Integer> ( | |
clubs | |
diamonds | |
spades | |
hearts | |
error | |
) | |
) | |
) | |
class Library ( | |
store ( | |
_books Vector<Book> | |
) | |
attach ( | |
typedef AddBookError Error | |
func addBook* (book Book) -> (error AddBookError) ( | |
error = this._books.append(book) | |
) | |
typedef RemoveBookError Error | |
func removeBook* (book Book) -> (error RemoveBookError) ( | |
error = this._books.remove*(book) | |
) | |
) | |
) | |
class Librarian ( | |
store ( | |
_library Library | |
) | |
attach ( | |
func Librarian (library* Library) ( | |
this._library = library* | |
) | |
typedef BorrowBookError Error | |
func borrowBook* (book Book) -> BorrowBookError ( | |
# найти экземпляр в библиотеке | |
# пометить экзампляр как отданый | |
) | |
typedef FindBookByNameError Error | |
func findBookByName (bookName String) -> (Book, FindBookByNameError) ( | |
# ... | |
) | |
) | |
) | |
class Client ( | |
borrow ( | |
basicPerson BasicPerson | |
) | |
store ( | |
_firstName String | |
_familyName String | |
_fatherName String | |
_birthDate Date | |
) | |
attach ( | |
func Client ( | |
firstName String | |
familyName String | |
fatherName String | |
) ( | |
# ??? | |
) | |
typedef BorrowBookError Error | |
func borrowBook* ( | |
librarian* Librarian | |
book Book | |
) -> error:BorrowBookError ( | |
error = librarian*.borrowBook*(book) | |
) | |
typedef ReturnBookError Error | |
func returnBorrowedBook ( | |
librarian Librarian | |
book Book | |
) -> ReturnBookError ( | |
# ??? | |
) | |
) | |
) | |
func _makeTestLibrary () -> (library Library) ( | |
books := Vector<Book> ( | |
Book ( | |
name = "The C Programming Language" | |
edition = 1 | |
publisherName = "Prentice Hall" | |
publishingDate = Date(year=1978) | |
authors = ("Dennis M. Ritchie" "Brian W. Kernighan") | |
) | |
Book ( | |
name = "Structure and Interpretation of Computer Programs" | |
edition = 2 | |
publisherName = "The MIT Press" | |
publishingDate = Date(year=1996) | |
authors = ("Harold Abelson" "Gerald Jay Sussman") | |
) | |
) | |
library.registerBooks*(books) | |
) | |
func main (arguments Array<String>) ( | |
library* := _makeTestLibrary() | |
librarian* := Librarian(library*=getPointer(library*)) | |
krBookName := "The C Programming Language" | |
krBook := ?librarian*.findBookByName(krBookName) | |
client* := Client ( | |
firstName = "Andrey" | |
familyName = "Lesnikov" | |
fatherName = "Alexandrovich" | |
) | |
?client*.borrowBook*(librarian*=librarian*, book=krBook) | |
?client*.returnBorrowedBook(librarian=librarian*, book=krBook) | |
) handle ( | |
case error:Librarian.FindBookByNameError ( | |
console.printLine*("Can not find book '", krBookName, "'!") | |
) | |
case error:Client.BorrowBookError ( | |
console.printLine*("Can not borrow book '", krBookName, "'!") | |
) | |
case error:Client.ReturnBorrowedBookError ( | |
console.printLine*("Can not return book '", krBookName, "'!") | |
) | |
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
clear | |
pep8 *.py --exclude=parsetab.py | |
# Example output: '71%' | |
run_one_module_unit_tests() { | |
echo -n $1': ' | |
cov="coverage run --branch --source=$1" | |
cmd="python -m $cov -m unittest test_$1" | |
run_report=$($cmd 2>&1) | |
# $cmd | |
# 1 2 3 4 5 6(!) | |
# Name Stmts Miss Branch BrMiss Cover | |
# generator 254 73 70 20 71% | |
python -m coverage report | grep $1 | awk '{print($6)}' | |
# generate html docs in separate folder | |
python -m coverage html -d htmlcov_$1 | |
} | |
# filelist='datatype generator misc parse' | |
# for file in $filelist; do run_one_module_unit_tests $file; done | |
python -B -m coverage run --branch -m unittest discover | |
python -B -m coverage html -d htmlcov | |
python -B -m coverage report | |
# rm *_out.c *_out.exe | |
# pylint --rcfile=.pylint.rc *.py -f html > x.html |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!env python | |
# -*- coding: utf-8 -*- | |
# See LICENSE file for copyright and license details | |
import subprocess | |
call = subprocess.call | |
check_output = subprocess.check_output | |
def run_one_module_unit_tests(file): | |
print file | |
call([ | |
'python', | |
# '-m', 'coverage', 'run', '--branch', | |
# '--source=', 'misery/' + file + '.py', | |
'-m', 'unittest', 'misery/test_' + file + '.py', | |
]) | |
# 1 2 3 4 5 6(!) | |
# Name Stmts Miss Branch BrMiss Cover | |
# generator 254 73 70 20 71% | |
# python -m coverage report | grep $1 | awk '{print($6)}' | |
# output = check_output(['python', '-m', 'coverage', 'report']) | |
# print file + ': ' + '\n' + output | |
# generate html docs in separate folder | |
# call(['python', '-m', 'coverage', 'html', '-d', 'htmlcov_' + file]) | |
filelist = [ | |
# 'datatype', | |
# 'generator', | |
# 'misc', | |
# 'parse', | |
] | |
for file in filelist: | |
run_one_module_unit_tests(file) | |
call(['pep8', 'misery', '--exclude=parsetab']) | |
call([ | |
'python', '-m', 'coverage', 'run', '--branch', | |
'-m', 'unittest', 'discover', | |
]) | |
call(['python', '-m', 'coverage', 'report']) | |
call(['python', '-m', 'coverage', 'html', '-d', 'htmlcov']) | |
# pylint --rcfile=.pylint.rc *.py -f html > x.html |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment