Перейти на главную страничку сайта (список статей, файлы для скачивания)

ФОРУМ (здесь можно обсудить эту статью, а также любые проблемы программирования на различных макроязыках и в скриптовых средах)

Язык программирования Boo

Язык программирования Boo — объектно-ориентированный язык программирования со статической типизацией для Common Language Infrastructure (CLI), т.е. для .NET и Mono, с открытой лицензией, питонообразным синтаксисом и упором на расширяемость компилятора и самого языка. В некотором смысле, Boo — это смесь Python и C#. Полный список отличий от Python можно найти здесь. У Boo есть интерактивный интерпретатор. По мнению некоторых, язык Воо имеет более компактный код и более высокую скорость исполнения, чем Python или IronPython. На Boo можно писать настольные приложения с графическим интерфейсом на основе Windows.Forms / GTK#, а также приложения ASP.NET. Взять язык можно здесь (дистрибутив "binary" на момент написания данной статьи имеет размер порядка 900 Кб).

Автор языка — Родриго Баррето де Оливейра (Rodrigo Barreto de Oliveira). Причины создания Boo его автор объясняет тем, что в Python ему не хватало статической типизации, проверки ошибок времени компиляции и продуманной архитектуры .NET. С другой стороны, синтаксис C# автора тоже полностью не устраивал, как слишком многословный в некоторых случаях. Кроме того, автор хотел иметь язык, который можно расширять собственными конструкциями, а также интерактивный интерпретатор, в котором можно быстро проверить фрагменты кода.

Установка: если вы скачали дистрибутив "binary", просто разархивируйте Boo, и вы получите компилятор booc.exe, интерпретатор booi.exe и интерактивный интерпретатор booish.exe. Естественно, необходимо иметь установленный .NET или MONO. Можете добавить каталог Boo к переменной PATH.

На официальном сайте языка вы найдёте примеры кода, руководства и ссылки на проекты на языке Boo.

Hello, World!

Создайте текстовый файл test.boo в кодировке utf-8 следующего содержания:

print("Привет, мир!")

Запустите скрипт на исполнение интерпретатором, командой наподобие следующей:

booi test.boo

Пример чтения с консоли:

import System
name = Console.ReadLine()
print("Hello, ${name}")

Простейший пример организации GUI (на Windows.Forms):

import System.Windows.Forms
f = Form(Text: 'Привет, Boo!')
button = Button()
button.Dock = DockStyle.Fill
button.Text = System.DateTime.Now.ToString()
button.add_Click({button.Text = System.DateTime.Now.ToString()})
f.Controls.Add(button)
Application.Run(f)

Пример выше выведет окно с единственной кнопкой, на которой отображается текущее время. Нажатие на кнопку будет обновлять время.

Вы можете откомпилировать последний пример командой наподобие следующей:

booc -target:winexe test.boo

Для успешного запуска полученного таким способом test.exe из любого каталога будет достаточно зарегистрировать сборку Boo.Lang.dll из поставки Boo в глобальном кеше сборок .NET (Global Assembly Cashe) с помощью утилиты gacutil.exe, примерно такой командой:

gacutil.exe /i Boo.Lang.dll

Результат — работоспособная исполняемая программа test.exe размером порядка 4 Кб.

Простейший пример использования COM-объекта:

def CreateInstance(progid):
    type = System.Type.GetTypeFromProgID(progid)
    return type()
# создаём "утку":
wsh as duck = CreateInstance("WScript.Shell")
# пробуем крякнуть по-утиному (вызвать метод):
wsh.popup("Привет, Windows Script Host & Boo!")

Простейший пример использования Win32 API:

import System.Runtime.InteropServices
[DllImport("User32.dll", EntryPoint:"MessageBox")]

def msgbox(hwnd as int, msg as string, caption as string, msgtype as int):
    pass

def msgbox(msg as string):
    msgbox(0, msg, "Сообщение", 0)

msgbox(0, "вызов MessageDialog", "демонстрация DllImport", 0)
msgbox("ещё раз")

Пример выше выведет поочерёдно два окна сообщения.

Пример вызова Win32 API с организацией обратного вызова (callback):

import System
import System.Runtime.InteropServices

class User32:
    callable EnumWindowProc(hwnd as IntPtr, lparam as IntPtr) as bool
    [DllImport("user32.dll")]
    static def EnumWindows(proc as EnumWindowProc, param as IntPtr) as bool:
        pass
    [DllImport("user32.dll", CharSet : CharSet.Auto)]
    static def GetWindowText(hWnd as IntPtr, [Out] title as string, maxCount as int) as int:
        pass

def proc(hwnd as IntPtr, lparam as IntPtr):
    if hwnd != IntPtr.Zero:
        s = string(char(' '), 260)
        User32.GetWindowText(hwnd, s, 260)
        s = s.Trim()
        if len(s) and cast(int, s[0]) != 0:
            print s
    return true

User32.EnumWindows(proc, IntPtr.Zero)

Пример выше выведет на консоль заголовки окон верхнего уровня, открытых в системе в настоящий момент.

Синтаксические особенности языка Boo

Питонообразность, но со статической типизацией; списки, хеши, итерируемые строки, инициализация объектов, форматирование строк, регулярные выражения. Нет кортежей, но есть массивы в стиле C#, очень на них похожие.

Автоматическое объявление переменных; присваивание вводит новую локальную переменную.

Автоматическая типизация, сокращающая код; например, следующая конструкция явно возвращает целое, и компилятор это понимает:

def one():
    return 1
um = one()

С другой стороны, при желании программист должен быть в состоянии точно указать, что он подразумевает:

def one() as int:
    return 1
uno as int = one()

В этом случае компилятор должен только проверить, имеет ли смысл заявленное. Другими словами, должна ожидаться ошибка компилятора для кода, подобного следующему:

def one():
    return "1"
ichi as int = one()

Автоматического приведения типов нет; по мнению автора языка, лучше протестировать код, чем полагаться на это.

Классы не являются необходимыми в каждой программе, писать сакраментальные конструкции типа "public static void main" (как в C#) не нужно, простейшая программа будет такой:

print("Hello, world!")

Выразительные возможности языка

Функции могут использоваться как возвращаемые значения:

def ignore(item):
    pass
def selectAction(item as int):
    return print if item % 2
    return ignore
for i in range(10):
    selectAction(i)(i)

Функции могут присваиваться переменным:

p = print
p("Hello, world!")

Функции могут выступать в роли объектов:

print.Invoke("Hello, world!")

Асинхронные вызовы делегатов:

import System
def callback(result as IAsyncResult):
    print("callback")
def run():
    print("executing")

print("started")
result = run.BeginInvoke(callback, null)
System.Threading.Thread.Sleep(50ms)
run.EndInvoke(result)
print("done")

Примечание: делегаты можно представлять себе как переменные, в которых можно хранить некоторые функции. Методы делегата BeginInvoke и EndInvoke позволяют вызывать делегат асинхронно. Обратный вызов (callback) позволяет произвести некоторое асинхронное действие для каждого вызова.

Определение вызываемых типов:

callable Malkovich() as Malkovich
def malkovich() as Malkovich:
    print("Malkovich!")
    return malkovich
malkovich()()()

Генераторы (языковые конструкции, способные к производству нескольких значений, когда используются в итеративном контексте):

oddNumbers = i for i in range(10) if i % 2

Такие конструкции могут быть использованы и в качестве аргументов вызываемых функций; во всех случаях оценка каждого внутреннего выражения случается только по требованию, поскольку генератор употребляется в цикле for.

Генераторы с использованием ключевого слова yield:

def fibonacci():
    a, b = 0, 1
    while true:
        yield b
        a, b = b, a+b
for index as int, element in zip(range(5), fibonacci()):
    print("${index+1}: ${element}")

Пример выше выведет первые 5 чисел Фибоначчи.

Генераторные методы:

import System.Xml
def selectElements(element as XmlElement, tagName as string):
    for node as XmlNode in element.ChildNodes:
        if node isa XmlElement and tagName == node.Name:
            yield node

Можно не заботиться о типизации (duck typing), если в этом нет необходимости:

import System.Threading
def CreateInstance(progid):
    type = System.Type.GetTypeFromProgID(progid)
    return type()
ie as duck = CreateInstance("InternetExplorer.Application")
ie.Visible = true
ie.Navigate2("http://www.script-coding.com/")
Thread.Sleep(50ms) while ie.Busy
document = ie.Document
print("${document.title} is ${document.fileSize} bytes long.")

Расширяемость языка

Работа с синтаксическими атрибутами сокращает код:

class Person:
    [getter(FirstName)]
    _fname as string
    [getter(LastName)]
    _lname as string
    def constructor([required] fname, [required] lname):
        _fname = fname
        _lname = lname

Вышеприведённый код приводит к тому же эффекту, что и следующий, более многословный:

import System
class Person:
    _fname as string
    _lname as string
    def constructor(fname, lname):
        raise ArgumentNullException("fname") if fname is null
        raise ArgumentNullException("lname") if lname is null
        _fname = fname
        _lname = lname
    FirstName as string:
        get:
            return _fname
    LastName as string:
        get:
            return _lname

Синтаксические макросы наращивают язык новыми конструкциями:

import System.IO
using reader=File.OpenText(fname):
    print(reader.ReadLine())

В данном случае "using" — синтаксическая макрокоманда, которая заставляет вышеприведённый код интерпретироваться компилятором как следующий:

try:
    reader = File.OpenText(fname)
    print(reader.ReadLine())
ensure:
    if (__disposable__ = (reader as System.IDisposable))
        __disposable__.Dispose()
        __disposable__ = null
    reader = null

Процесс самой компиляции расширяем. Например, следующий код определяет новый шаг компиляции, который проверяет, что у каждого класса, определённого в программе, есть свое название, начинающееся с заглавной буквы:

namespace StyleChecker
import Boo.Lang.Compiler
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.Steps
import Boo.Lang.Compiler.Pipelines

class StyleCheckerStep(AbstractVisitorCompilerStep):
    override def Run():
        Visit(CompileUnit)
    override def LeaveClassDefinition(node as ClassDefinition):
        if not char.IsUpper(node.Name[0]):
            msg = "Class name '${node.Name}' should start with uppercase letter!"
            Errors.Add(CompilerError(node, msg))

class StyleCheckerPipeline(CompileToFile):
    def constructor():
        self.Insert(1, StyleCheckerStep())

Людоговский Александр, 23.01.2009г.

Перейти на главную страничку сайта (список статей, файлы для скачивания)

© 2007 http://www.script-coding.com При любом использовании материалов сайта обязательна ссылка на него как на источник информации, а также сохранение целостности и авторства материалов.