Bird 编程语言:第二部分






4.85/5 (10投票s)
一种新的通用编程语言,旨在实现快速、高级且易于使用。(我将其从 Anonymus 重命名为 Bird)
关于 Bird 的文章
- Bird 编程语言:第一部分
- Bird 编程语言:第二部分
- Bird 编程语言:第三部分
目录
第一个示例程序:正方形
在此示例中,我绘制了逐渐消失的正方形。当鼠标悬停在正方形上方时,它会出现。
我创建了一些常量和一个数组,其中包含一个正方形的可见度。
using System
using BlitzMax
namespace SquaresSample
const var SquareSize = 32,
Width = 24,
Height = 20
float[Width, Height] Array
IsMouseOver
函数在鼠标悬停在指定区域时返回 true。
bool IsMouseOver(int x, y, w, h)
return x <= MouseX() < x + w and y <= MouseY() < y + h
Update
函数绘制正方形并更新数组的值。它将亮度存储在 Value
变量中,正方形的位置存储在 XPos
和 YPos
中。
void Update()
for var x, y in 0 .. (Width, Height)
var Value = Array[x, y]
var XPos = x * SquareSize
var YPos = y * SquareSize
如果鼠标悬停在正方形上,则将 Value
设置为 1
,这是最大亮度,并绘制一个白色矩形:
if IsMouseOver(XPos, YPos, SquareSize, SquareSize)
Value = 1
SetColor 255, 255, 255
DrawRect XPos, YPos, SquareSize, SquareSize
如果鼠标不在正方形上且正方形不是黑色,则绘制具有适合其位置和亮度的颜色的矩形。
else if Value > 0
var Color = 255 * Value
var Red = (int)(((float)x / Width) * Color)
var Greed = (int)(((1 - (float)y / Height)) * Color)
var Blue = (int)(((float)y / Height) * Color)
SetColor Red, Greed, Blue
DrawRect XPos, YPos, SquareSize, SquareSize
它将 Value
变量递减直到 0
并将其存储在数组中。
Array[x, y] = Math.Max(Value - 0.02, 0)
程序从 Main
函数开始。它使用 Graphics
创建图形窗口。在一个循环中,它不断地清除、重绘和更新屏幕(默认每秒 60 次)。可以通过按 Escape 键或关闭窗口来中断。
void Main()
Memory.Zero Array, sizeof(Array)
Graphics SquareSize * Width, SquareSize * Height
while !KeyHit(Keys.Escape) and !AppTerminate()
Cls
Update
Flip
第二个示例程序:圆形
此示例计算每个像素的颜色,因此可以与 C++ 进行小范围比较。为了提高性能,它在比窗口小的图像上进行计算,然后将其放大。
我做了一些辅助函数。ArgbColor
函数将颜色的四个 8 位分量组合成一个 32 位数字。
using System
using BlitzMax
namespace CirclesSample
int ArgbColor(byte a, r, g, b)
return (a to int) << 24 | (r to int) << 16 |
(g to int) << 8 | b
GetDistance
计算两点之间的距离。
double GetDistance(double x1, y1, x2, y2)
var x = x2 - x1, y = y2 - y1
return Math.Sqrt(x * x + y * y)
Clamp
函数从整数创建一个介于 0
和 255
之间的字节数。
byte Clamp(int x)
if x < 0: x = 0
if x > 255: x = 255
return (byte)x
主要任务由 UpdateImage
完成。它首先调用 LockImage
使图像可修改,并存储自计算机启动以来已过多少秒。
void UpdateImage(IntPtr Image)
var Pixmap = LockImage(Image, 0, true, true)
var Width = ImageWidth(Image)
var Height = ImageHeight(Image)
var Time = MilliSecs() / 1000.0
它使用 for 循环遍历所有像素,计算其相对位置,以及与屏幕中心点的距离(减去时间)。
for var x, y in 0 .. (Width, Height)
var RelX = (x to double) / Width
var RelY = (y to double) / Height
var Value = GetDistance(RelX, RelY, 0.5, 0.5) * 3 – Time
从该值计算出颜色。
var Light = ((RelY * 100) * Math.Abs(Math.Sin(Value / 1.5)) to int)
var Red = ((RelX * 255) * Math.Abs(Math.Cos(Value)) to int) + Light
var Green = (((1 - RelY) * 255) * Math.Abs(Math.Sin(Value)) to int) + Light
var Blue = ((RelY * 255) * Math.Abs(Math.Cos(Value / 3)) to int) + Light
它存储像素,并在循环结束时调用 UnlockImage
函数使图像再次可绘制。
var Color = ArgbColor(255, Clamp(Red), Clamp(Green), Clamp(Blue))
WritePixel Pixmap, x, y, Color
UnlockImage Image
Main
函数与前一个示例唯一的区别是它创建了被放大的图像,并显示了 FPS。
void Main()
Graphics 1024, 720
var Image = CreateImage(512, 360)
while not KeyHit(Keys.Escape) and not AppTerminate()
Cls
UpdateImage Image
DrawImageRect Image, 0, 0, 1024, 720
DrawFrameStats
Flip
这是在我 Intel Core 2 Dou 1.8 Ghz 处理器上的性能结果(每秒帧数)。
图像尺寸 | G++ 4.6.2 | Clang (LLVM 3.1) | Visual C++ 11 | Bird |
320×240 | 17 | 19 | 27 | 34 |
分析生成的汇编代码
ArgbColor 函数
此函数有四个参数,按此顺序存储在 al
、ah
、dl
、dh
寄存器中,返回值存储在 eax
中。(a to int) << 24
表达式也存储在 eax
中,因为目标变量是同一个寄存器。但它会覆盖 ah
的值,因此必须将其复制到 cl
。其他子表达式可以存储在 ecx
中而不覆盖任何内容。
_CirclesSample_ArgbColor:
mov cl, ah
and eax, 0xFF
shl eax, 24
and ecx, 0xFF
shl ecx, 16
or eax, ecx
movzx ecx, dl
shl ecx, 8
or eax, ecx
movzx ecx, dh
or eax, ecx
ret
GetDistance 函数
前三个参数存储在 xmm0
、xmm1
、xmm2
中,第四个参数在堆栈上。返回值存储在 xmm0
中。为了提高性能,堆栈指针并不总是保存到 ebp
。
_CirclesSample_GetDistance:
movsd xmm3, xmm0
movsd xmm0, xmm2
subsd xmm0, xmm3
movsd xmm3, qword[esp + 4]
subsd xmm3, xmm1
movsd xmm1, xmm0
mulsd xmm1, xmm0
movsd xmm2, xmm3
mulsd xmm2, xmm3
addsd xmm1, xmm2
sqrtsd xmm0, xmm1
ret 8
Clamp 函数
编译器在这种情况下使用条件移动指令来避免条件跳转。cmov
指令需要第二个参数是一个内存位置。
_CirclesSample_Clamp:
cmp eax, 0
cmovl eax, dword[_25]
cmp eax, 255
cmovg eax, dword[_27]
ret
第三个示例程序:火焰
在此示例中,我为像素创建了一个浮点数数组。当鼠标靠近时,这些值会增加,同时也会模糊图像。我将这些值的颜色存储在另一个数组中。这些是我创建的常量和数组。
using System
using BlitzMax
namespace FireSample
const var
WindowSize = (1024, 768),
ImageSize = (560, 420),
RectSize = 80,
ColorNumber = 768
float[ImageSize.0, ImageSize.1] Array
int[ColorNumber] Colors
在此示例中,我使用了元组,WindowSize
和 ImageSize
的类型是 (int, int)
。
我使用了前一个示例中的两个函数。
int ArgbColor(byte a, r, g, b)
return (a to int) << 24 | (r to int) << 16 | (g to int) << 8 | b
byte Clamp(int x)
if x < 0: x = 0
if x > 255: x = 255
return x to byte
我更改了 GetDistance
函数的参数类型为元组。
float GetDistance(float2 P1, P2)
var x = P1.x - P2.x, y = P1.y - P2.y
return Math.Sqrt(x * x + y * y) to float
GetValue
函数在指定位置返回数组的值(如果有效),否则返回零。
float GetValue(int x, y)
if 0 <= x < ImageSize.0 and 0 <= y < ImageSize.1
return Array[x, y]
return 0f
UpdateValues
函数更新 Array
的值。首先,它通过附近像素的加权平均值模糊图像。我将总和除以 7.1
而不是 7
以使其逐渐消失。
void UpdateValues()
for var x, y in 0 .. ImageSize
var Value = Array[x, y] * 5
Value += GetValue(x + 1, y + 1) * 0.5
Value += GetValue(x - 1, y + 1) * 0.75
Value += GetValue(x - 2, y + 2) * 0.5
Value += GetValue(x - 3, y + 3) * 0.25
Array[x, y] = Value / 7.1
它计算鼠标和将要在其中工作的正方形的相对位置。Max
函数返回两个数中较大的一个,Min
返回较小的一个。如果参数不是标量数字,它会对所有成员执行相同的操作。参数 0
会自动转换为 (0, 0)
。
var Mouse = (GetMousePosition() to float2) * ImageSize / WindowSize
var P1 = Math.Max((Mouse to int2) - RectSize, 0)
var P2 = Math.Min((Mouse to int2) + RectSize, ImageSize - 1)
计算每个点的距离,并根据距离增加像素的亮度。最大值为一。
for var x, y in P1 .. P2
var Dist = 1 - GetDistance(Mouse, (x, y)) / RectSize
if Dist >= 0f: Array[x, y] = Math.Min(Array[x, y] + Dist / 10f, 1)
UpdateImage
函数使用 Array
中存储的值更新图像。
void UpdateImage(IntPtr Image)
var Pixmap = LockImage(Image)
for var x, y in 0 .. ImageSize
var Color = Array[x, y] * (ColorNumber - 1) to int
WritePixel Pixmap, x, y, Colors[Color]
UnlockImage Image
此函数计算值的颜色。
void Initialize()
for var i in 0 .. ColorNumber
var Value = (i to float) / (ColorNumber - 1)
var Red = (Math.Min(Value, 1f / 3)) * 3 * 255 to int
var Green = (Math.Min(Value, 1.65f / 3) - 0.65f / 3) * 3 * 255 to int
var Blue = (Math.Min(Value, 1f) - 2f / 3) * 3 * 255 to int
Colors[i] = ArgbColor(255, Clamp(Red), Clamp(Green), Clamp(Blue))
程序的 main 函数与前几个示例类似。它调用 UpdateValues
和 UpdateImage
函数。
void Main()
Initialize
Graphics WindowSize
var Image = CreateImage(ImageSize)
while not KeyHit(Keys.Escape) and not AppTerminate()
Cls
Update
UpdateImage Image
DrawImageRect Image, (0, 0), WindowSize
DrawFrameStats
Flip
所有编译器的性能都差不多,但 C++ 版本要长得多。
第四个示例程序:反射
此示例展示了如何使用低级反射。我计划将来在此基础上创建一个更高级的层。
我创建了两种标识符。第一种是声明的标识符,通常是类、函数、变量等。它们有名称。另一组是没有声明的,由编译器生成,例如在获取变量地址或创建元组时。
将成员写入屏幕
第一个函数输出了 Internals.Reflection.Reflection
类的五个成员。为此,它读取了编译器为其生成的反射数据,ReadDeclaredId
函数返回一个 DeclaredIdData
结构。它包含标识符的名称、其成员等。也可以对成员调用它以获取它们的名称。
using System
using Internals
using Internals.Reflection
namespace ReflectionSamples
void ListMembers()
Console.WriteLine "-ListMembers-----------------------------------------"
var IdData = Reflection.ReadDeclaredId(id_desc_ptr(Reflection))
var Count = Math.Min(5u, IdData.Members.Length)
for var i in 0 .. Count
var IdData2 = Reflection.ReadDeclaredId(IdData.Members[i])
Console.WriteLine IdData2.Name
IdData2.Free
IdData.Free
Console.WriteLine
输出
-ListMembers------------------------------------------- EntryAssembly GetAliasBase GetRealId IsEquivalentHelper GetDeclaredEquivalent
比较两种类型
下一个函数输出对象的类型并将其与另一个对象进行比较。WriteLine
函数在对象不是字符串时调用对象的 ToString
方法。默认情况下,它返回类型的名称。
void TypeCompare()
object Obj = (0, 1, 2)
Console.WriteLine "-TypeCompare-----------------------------------------"
Console.WriteLine Obj
IDENTIFIER_PTR Type = id_desc_ptr((int, int, int))
Console.WriteLine Reflection.IsEquivalent(ObjectHelper.GetType(Obj),Type)
Console.WriteLine
输出
-TypeCompare------------------------------------------- (int, int, int) True
修改变量
此函数通过 DeclaredIdData.Address
修改变量的值。
int GlobalVariable
void SettingGlobal()
Console.WriteLine "-SettingGlobal-----------------------------------------"
var Id = id_desc_ptr(GlobalVariable)
var IdData = Reflection.ReadDeclaredId(Id)
*(int*)IdData.Address = 123123
IdData.Free
Console.WriteLine GlobalVariable.ToString()
Console.WriteLine
输出
-SettingGlobal----------------------------------------- 123123
调用函数
CallingFunction
将 DeclaredIdData.Address
成员转换为函数指针并调用它。
void CallingFunction()
Console.WriteLine "-CallingFunction-------------------------------------"
var Id = id_desc_ptr(Function)
var IdData = Reflection.ReadDeclaredId(Id)
var FuncPtr = IdData.Address to (static string -> void)
IdData.Free
FuncPtr "Function called"
Console.WriteLine
void Function(string Str)
Console.WriteLine "Str = " + Str
输出
-CallingFunction--------------------------------------- Str = Function called
函数参数
所有变量的类型都是未声明的标识符。它们由 UndeclaredIdData
结构描述。Type
成员指定了标识符的类型。例如,它可以是 UndeclaredIdType.Pointer
。所有已声明的标识符都可以用 UndeclaredIdType.Unknown
值来引用,UndeclaredIdData.DeclaredId
成员存储其指针。
FunctionData
首先确保给定的标识符是一个函数。
void FunctionData(IDENTIFIER_PTR Id)
Console.WriteLine "-FunctionData----------------------------------------"
var IdData = Reflection.ReadDeclaredId(Id)
if IdData.Type != DeclaredIdType.Function
Console.WriteLine "Not a function"
IdData.Free
如果它是一个函数,则读取标识符的类型数据。函数的类型包含返回值、参数等。它使用 GetUndeclIdName
函数来确定其类型的名称,这些类型也是未声明的标识符。
else
UNDECLARED_ID_PTR FuncType = IdData.BaseUndeclaredId
IdData.Free
var TypeData = Reflection.ReadUndeclaredId(FuncType)
Console.WriteLine "Function: " + Reflection.GetFullName(Id)
Console.WriteLine "Return type: " + GetUndeclIdName(TypeData.BaseUndeclaredId)
Console.WriteLine "Parameters:"
for var i in 0 .. TypeData.Parameters.Length
var ParamType = GetUndeclIdName(TypeData.Parameters[i].UndeclaredType)
var ParamName = TypeData.Parameters[i].Name
Console.WriteLine ParamType + " " + ParamName
TypeData.Free
确定未声明标识符的名称并不简单,因此它只在其引用已声明标识符或具有已声明等效项(如 UndeclaredIdType.String
)时才返回其名称。
string GetUndeclIdName(UNDECLARED_ID_PTR UndeclId)
var UndeclData = Reflection.ReadUndeclaredId(UndeclId)
var UndeclType = UndeclData.Type
if UndeclType == UndeclaredIdType.Unknown
var DeclId = UndeclData.DeclaredId
UndeclData.Free
return Reflection.GetFullName(DeclId)
else
UndeclData.Free
var DeclId = Reflection.GetDeclaredEquivalent(UndeclType)
if DeclId != null: return Reflection.GetFullName(DeclId)
return "???"
输出
-FunctionData------------------------------------------ Function: ReflectionSamples.GetUndeclIdName Return type: System.String Parameters: Internals.Reflection.UNDECLARED_ID_PTR UndeclId
Main 函数
它调用前面示例的函数。
void Main()
ListMembers
TypeCompare
SettingGlobal
CallingFunction
FunctionData id_desc_ptr(GetUndeclIdName)