65.9K
CodeProject 正在变化。 阅读更多。
Home

Bird 编程语言:第二部分

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.85/5 (10投票s)

2012 年 9 月 22 日

GPL3

6分钟阅读

viewsIcon

29396

downloadIcon

379

一种新的通用编程语言,旨在实现快速、高级且易于使用。(我将其从 Anonymus 重命名为 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 变量中,正方形的位置存储在 XPosYPos 中。

    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 函数从整数创建一个介于 0255 之间的字节数。

    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 函数

此函数有四个参数,按此顺序存储在 alahdldh 寄存器中,返回值存储在 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 函数

前三个参数存储在 xmm0xmm1xmm2 中,第四个参数在堆栈上。返回值存储在 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

在此示例中,我使用了元组,WindowSizeImageSize 的类型是 (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 函数与前几个示例类似。它调用 UpdateValuesUpdateImage 函数。

    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

调用函数

CallingFunctionDeclaredIdData.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)
© . All rights reserved.