Pawn

Материал из Национальной библиотеки им. Н. Э. Баумана
Последнее изменение этой страницы: 20:10, 1 июня 2016.
Pawn
Спроектировано ITB CompuPhase
Первый   появившийся 1998
Стабильная версия 4.0.4733 / 22 июня 2012
OS Кроссплатформенный
Лицензия Apache 2.0 с исключением линкования
Расширение файла .pwn, .p, .amx
Портал: pawn
Под влиянием
Small-C

Pawn (прежнее название — Small) — простой, не типизированный 32-битный скриптовый язык программирования с синтаксисом, подобным языку C++. Компилятором генерируется P-код (или байт-код), который запускается на абстрактной машине. Файлы исходного кода Pawn имеют расширение .pwn. Скомпилированные и готовые к исполнению файлы получают расширение .amx

История языка

Введение в язык Pawn и в понятие абстрактной машины было описано как перспектива программистов и опубликовано в октябре 1999 года в журнале доктора Добба. Язык разрабатывался для начинающих программистов, допускающих ошибки, связанные с освобождением памяти компьютера. Основа для Pawn - язык Си. Целью разработчиков было упростить и изменить язык Си таким образом, чтобы с помощью синтаксиса языка можно было избегать или обходить те ошибки, допускаемые неопытными программистами. Выбор языка Си определило то, что этот язык в тот момент достиг максимума своего развития и был довольно популярен.

Особенности языка

  • Pawn - это Си-подобный скриптовый язык
  • Вы можете подключать файлы (include) в Pawn, так что вы сможете организовать аккуратную структуру кода.
  • Pawn - это скриптовый язык с компилятором, который выполняет статические проверки и с абстрактной машиной, которая (статически) выполняет верификацию P-кода и динамическую проверку.
  • С целью портирования, Pawn был написан на ANSI C
  • Pawn поддерживает Unicode/UCS-4 и UTF-8, а также кодовые страницы. Компилятор может преобразовать введённый исходный код в особую кодовую страницу Unicode; он также поддерживает файлы исходного кода в кодировке UTF-8.
  • Он был установлен на микроконтроллеры Atmel ATmega128, Philips LPC2138 и LPC2106 (ядро ARM7TDMI с 32 КБ ОЗУ), а также на Texas Instrument MSP430F1611 (ядро MSP430 с 10 КБ оперативной памяти и 48 КБ Flash ROM), так как позволяет запускать большие скрипты с небольшим объемом памяти.
  • Документирование исходного кода может быть сделано с помощью "комментариев документации;", компилятор Pawn извлекает эти комментарии, объединяет их с информацией из исходного кода и записывает XML-файл, который можно просмотреть (и распечатать) с помощью веб-браузера.
  • Pawn поддерживает состояния и автоматоны, включая переменные локальных состояний.

Типы данных

В языке Pawn существует 4 типа данных:

  • Целые числа, Integer
  • Числа с плавающей точкой
  • Логический тип, булев или Boolean
  • Символ

Арифметика

Основные элементы большинства программ - вычисления, решения (условное выполнение), итерации (циклов), и переменные для хранения входных данных, выходных данных и промежуточных результатов. В следующем примере программа иллюстрирует многие из этих понятий. Программа вычисляет наибольший общий делитель двух значений с помощью алгоритма, изобретенный Евклидом.

/*
Поиск наибольшего общего делителя двух чисел,
с помощью алгоритма Евклида.
*/
main()
{
	print "Input two values\n"
	new a = getvalue()
	new b = getvalue()
	while (a != b)
		if (a > b)
			a = a - b
		else
			b = b - a
	printf "The greatest common divisor is %d\n", a
}

Функция main теперь содержит больше, чем просто один вызов функции "print". Когда тело функции содержит более одного оператора, эти операторы должны быть помещены в фигурные скобки - "{" и "}" символы. Это группирует инструкции к одной инструкции соединения. Функция getvalue (общая предопределенная функция) считывает значение с клавиатуры и возвращает результат.

Массивы и константы

Наряду с простыми переменными, размером в одну ячейку, pawn поддерживает "массив переменных", которые содержат много пар ячейка / значение. В следующем примере программа отображает ряд простых чисел, используя хорошо известный алгоритм построения "решета Эратосфена". Программа также вводит еще новую концепцию: символические константы. Символьные константы похожи на переменные, но они не могут быть изменены.

/* Выводит все простые числа меньше 100, используя "Решето Эратосфена" */
main()
{
	const max_primes = 100
	new series[max_primes] = { true, ... }
	for (new i = 2; i < max_primes; ++i)
		if (series[i])
		{
			printf "%d ", i
			/* отфильтровать все множители этого простого числа */
			for (new j = 2 * i; j < max_primes; j += i)
				series[j] = false
		}
}

Функции

Большие программы разделяют отдельные задачи и операции в функции. Использование функций повышает модульность программ и функций, когда хорошо написаны, портируемы в другие программы. В следующем примере реализуется функция для вычисления чисел из ряда Фибоначчи. Последовательность Фибоначчи была открыта Леонардо Фибоначчи из Пизы, итальянский математик 13 века, чьим величайшим достижением было популяризация для западного мира Хинду-арабских цифр. Цель последовательности была описать рост популяции (идеализированных) кроликов; и последовательность 1, 1, 2, 3, 5, 8, 13, 21,. , , (каждое следующие значение является суммой двух предыдущих).

/* Итерационное вычисление чисел Фибоначчи */
main()
{
	print "Enter a value: "
	new v = getvalue()
	if (v > 0)
		printf "The value of Fibonacci number %d is %d\n",
		v, fibonacci(v)
	else
		printf "The Fibonacci number %d does not exist\n", v
}fibonacci(n)
{
	assert n > 0
	new a = 0, b = 1
	for (new i = 2; i < n; i++)
	{
		new c = a + b
		a = b
		b = c
	}
	return a + b
}

Инструкция assert в верхней части функции Фибоначчи заслуживает отдельного упоминания; она защищает от "невозможных" или недействительных значений. Отрицательное число Фибоначчи является недействительным, и assert выражение сигнализирует об ошибке программиста, если этот случай когда нибудь произойдет. Asserts должны только сигнализировать об ошибках программиста и никогда об ошибках ввода пользователем.

Реализация пользовательской функции не сильно отличается от реализации функции main. Функция Фибоначчи показывает два новых понятия, хотя: он получает входное значение с помощью параметра и возвращает значение (она имеет "результат"). Параметры функции объявляются в заголовке функции; единственным параметром в этом примере является "N". Внутри функции параметр ведет себя как локальная переменная, но значение которого передается извне в вызове функции. Оператор return завершает выполнение функции и устанавливает результат функции. Он не обязательно должен появляться в самом конце функции; раннее завершение функции допускается.

Рациональные числа

Все расчеты, сделанные до этого момента, использовали только целые числа. Pawn также имеет поддержку для чисел, которые могут вместить дробные значения: они называются "рациональными числами". Тем не менее, включена ли эта поддержка зависит от хост-приложения. Рациональные числа могут быть реализованы либо как с плавающей точкой или числа с фиксированной точкой. Арифметика с плавающей точкой обычно используется для общего назначения и научных расчетов, в то время как арифметика с фиксированной точкой больше подходит для финансовой обработки и применения, где ошибки округления не должны вступать в игру (или, по крайней мере, они должны быть предсказуемыми). Pawn инструментарий имеет модуля для работы и с плавающей точкой и с фиксированной запятой. Детали для этих модулей можно найти в соответствующей документации. Однако, хост может реализовать модуль для работы либо с плавающей точкой или фиксированной точкой, или оба, или не то и не другое Ниже программа требует, чтобы по крайней мере один вид рациональных чисел поддерживался; иначе она не будет работать, если хост-приложение не поддерживает рациональные числа в принципе.

#include <rational>
main()
{
	new Rational: Celsius
	new Rational: Fahrenheit
	print "Celsius\t Fahrenheit\n"
	for (Celsius = 5; Celsius <= 25; Celsius++)
	{
		Fahrenheit = (Celsius * 1.8) + 32
		printf "%r \t %r\n", Celsius, Fahrenheit
	}
}

Строки

Pawn не имеет встроенного типа "строка"; символьные строки хранятся в массивах, с конвенцией, что элемент массива за последним действительным символом равен нулю. Поэтому работа со строками эквивалентна работе с массивами. Программа ниже разделяет строку текста на отдельные слова и считает их количество. Это простая программа показывает возможности языка Pawn.

/* Считает количество слов, которые вводит пользователь */
	#include <string>
	main()
	{
		print "Please type a string: "
		new string[100]
		getstring string, sizeof string
		new count = 0
		new word[20]
		new index
		for ( ;; )
		{
			word = strtok(string, index)
			if (strlen(word) == 0)
				break
			count++
			printf "Word %d: ’%s’\n", count, word
		}
		printf "\nNumber of words: %d\n", count
	}	strtok(const string[], &amp;index)
	{
		new length = strlen(string)
		/* пропускает пробелы до слова */
		while (index < length &amp;&amp; string[index] <=  )
			index++
		/* сохраняем слово буква за буквой */
		new offset = index /* сохраняем начальную позицию слова */
		new result[20] /* строка, в которую сохраняется слово */
		while (index < length &amp;&amp; string[index] >   &amp;&amp; index - offset < sizeof result - 1)
		{
			result[index - offset] = string[index]
			index++
		}
		result[index - offset] = EOS /* Завершаем строку нулем */
		return result
	}

Массивы и символьные индексы (структурированные данные)

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

/* Очередь с приоритетами (для простых текстовых строк) */
#include <string>
main()
{
	new msg[.text{40}, .priority]
	/* Вставка нескольких элементовs (считываем с консоли) */
	printf "Please insert a few messages and their priorities; " ...
	"end with an empty string\n"
	for ( ;; )
	{
		printf "Message: "
		getstring msg.text, .pack = true
		if (strlen(msg.text) == 0)
		break
		printf "Priority: "
		msg.priority = getvalue()
		if (!insert(msg))
		{
			printf "Queue is full, cannot insert more items\n"
			break
		}
	}	/* Теперь выводи сообщения, изъятые из очереди */
	printf "\nContents of the queue:\n"
	while (extract(msg))
		printf "[%d] %s\n", msg.priority, msg.text
}const queuesize = 10
new queue[queuesize][.text{40}, .priority]
new queueitems = 0insert(const item[.text{40}, .priority])
{
	/* Проверяем, может ли поместить в очередь еще одно сообщение */
	if (queueitems == queuesize)
		return false /*очередь заполнена */
	/* Находим позицию, в которую произведем вставку */
	new pos = queueitems /* начинаем с конца */
	while (pos > 0 &amp;&amp; item.priority > queue[pos-1].priority)
		--pos /* Больший приоритет: перемещаем на одну позицию ближе к началу */
	/* Выделяем память для нового элемента */
	for (new i = queueitems; i > pos; --i)
		queue[i] = queue[i-1]
	/* Кладем сообщение в выделенное место */
	queue[pos] = item
	queueitems++
	return true
}extract(item[.text{40}, .priority])
{
	/* Проверяем, если ли в очереди еще одно сообщение */
	if (queueitems == 0)
		return false /* очередь пуста */
	/* копируем первый элемент */
	item = queue[0]
	--queueitems
	/* Продвигаем очередь на одну позицию вперед */
	for (new i = 0; i < queueitems; ++i)
		queue[i] = queue[i+1]
	return true
}

Функция main начинается с декларации переменной массива msg. Массив имеет два поля, ".text" и ".priority"; поле ".text" объявлено как подмассив, содержащий 40 символов. Этот период необходим для символических индексов и не может быть никакого пробела между периодом и именем. Когда массив объявляется с символическими индексов, то он может быть проиндексированы только с этими индексами. Это было бы ошибкой говорить, например, "msg[0]". С другой стороны, так как может быть только один символический индекс в скобках, скобки стали необязательными. То есть, вы можете написать "msg.priority" как сокращение для "msg.[priority]". Далее в main два цикла. Цикл for читает строки и значения приоритета из консоли и вставляет их в очередь. Цикл while ниже извлекает элемент за элементом из очереди и выводит информацию на экран. Стоит отметить то, что цикл for содержит и строку, и номер приоритета (целое число) в одной переменной msg; действительно, функция main объявляет только одну переменную. Функция getstring сохраняет текст сообщения, который вы вводите, начиная с массива msg.text в то время как значение приоритета сохраняется (присваиванием несколькими строками ниже) в msg.priority. Функция printf в цикле while читает строку и значение из этих позиций тоже.

Примеры кода

"Hello World" выглядит так же, как и в Си:

#include <core>
main() 
{
    print("Hello World!");
    return 1; // Возвращает 1
}

Пример создания и использования массива для целочисленных значений

#include <core>
main() 
{
    new array[4]; // Инициализация массива с 4 элементами
    array[0] = 43; // Изменение значения элемента с индексом 0
    array[1] = 10; // Изменение значения элемента с индексом 1
    array[2] = 799; // Изменение значения элемента с индексом 2
    array[3] = 1212; // Изменение значения элемента с индексом 3
    return 1; // Возвращает 1
}

Пример цикла и оператора if-else

#include <core>
main() 
{
    for(new i = 0; i < 10; i++)
    {
        if(i != 9) printf("%d,", i);
        else print("девять");
    }
    return 1; // Возвращает 1
}

Ссылки

  1. The Pawn language
  2. Language Guide
  3. The Pawn language - language and toolkit features

Автор статьи: Макаров Д. В.