The Beef Programming Language

5 months ago 25
  • C/C++ interop with static and dynamic libs
  • Custom allocators
  • Batched allocations
  • Compile-time function evaluation
  • Compile-time code generation
  • Tagged unions
  • Generics
  • Tuples
  • Reflection
  • Properties
  • Lambdas
  • Valueless method references
  • Defer statements
  • SIMD support
  • Type aliases
  • Type extensions
  • Pattern matching
  • Ranges
  • String interpolation
  • Argument cascades
  • Mixin methods
  • Interfaces
  • Custom attributes
  • Immutable values
  • Operator overloading
  • Namespaces
  • Bitsets
  • Atomics
  • Checked arithmetic
  • Value boxing
  • Dynamic FFI
  • Local methods
  • Preprocessor
  • Guaranteed inlining
  • Incremental compilation
  • Built-in testing
using System; class Program { public static void Main() { Console.WriteLine("Hello, World!"); } }
// Try! propagates file and parsing errors down the call stack static Result<void> Parse(StringView filePath, List<float> outValues) { var fs = scope FileStream(); Try!(fs.Open(filePath)); for (var lineResult in scope StreamReader(fs).Lines) { for (let elem in Try!(lineResult).Split(',')) outValues.Add(Try!(float.Parse(elem))); } return .Ok; }
// Method returning a tuple (float x, float y) GetCoords => (X, Y); var tup = GetCoords; if (tup != (0, 0)) Draw(tup.x, tup.y); // Decompose tuple var (x, y) = GetCoords; Draw(x, y);
for (let i in 10...20) Console.WriteLine($"Value: {i}"); let range = 1..<10; Debug.Assert(range.Contains(3)); Span<int> GetLast10(List<int> list) => list[...^10];
// Allocate a string with a 4096-byte internal UTF8 buffer, all on the stack var str = scope String(4096); // String interpolation, formatting in 'x' and 'y' values var str2 = scope $"x:{x} y:{y}"; // Create a view into str2 without the first and last character StringView sv = str2[1...^1]; // Get a pointer to a null-terminated C string char8* strPtr = str2;
enum Shape { case None; case Square(int x, int y, int width, int height); case Circle(int x, int y, int radius); } Shape drawShape = .Circle(10, 20, 5); switch (drawShape) { case .Circle(0, 0, ?): HandleCircleAtOrigin(); case .Circle(let x, let y, let radius): HandleCircle(x, y, radius); default: } if (drawShape case .Square) HandleSquare();
void Draw(List<Variant> values) { int idx = 0; float NextFloat() { return values[idx++].Get<float>(); } DrawCircle(NextFloat(), NextFloat(), NextFloat()); }
// This class uses 'append' allocations, which allows a single "batch" // allocation which can accomodate the size of the 'Record' object, the // 'mName' String (including character data), and the 'mList' list, // including storage for up to 'count' number of ints class Record { public String mName; public List<int> mList; [AllowAppend] public this(StringView name, int count) { var nameStr = append String(name); var list = append List<int>(count); mName = nameStr; mList = list; } } // The following line results in a single allocation of 80080 bytes var record = new Record("Record name", 10000);
[CRepr] struct FileInfo { c_short version; c_long size; c_char[256] path; } /* Link to external C++ library method */ [CallingConvention(.Cdecl), LinkName(.CPP)] extern c_long GetFileHash(FileInfo fileInfo); /* Import optional dynamic method - may be null */ [Import("Optional.dll"), LinkName("Optional_GetVersion")] static function int32 (char8* args) GetOptionalVersion;
void Serialize(SerializeContext ctx, Object obj) { for (let field in obj.GetType().GetFields()) { Variant v = field.GetValue(obj); ctx.Serialize(field.Name, v); if (let attr = field.GetCustomAttribute<OnSerializeAttribute>()) { var m = attr.mSerializeType.GetMethod("SerializeField").Value; m.Invoke(null, obj, field); } } }
static mixin Inc(var val) { if (val == null) return false; (*val)++; } static bool Inc3(int* a, int* b, int* c) { // "return false" from mixin is injected into the Inc3 callsite Inc!(a); Inc!(b); Inc!(c); return true; }
// Declare List<T>.DisposeAll() for all disposable types namespace System.Collections { extension List<T> where T : IDisposable { public void DiposeAll() { for (var val in this) val.Dispose(); } } }
static int32 Factorial(int32 n) => n <= 1 ? 1 : (n * Factorial(n - 1)); const int fac = Factorial(8); // Evaluates to 40320 at compile-time public static Span<int32> GetSorted(String numList) { List<int32> list = scope .(); for (var sv in numList.Split(',')) list.Add(int32.Parse(sv..Trim())); return list..Sort(); } const int32[?] iArr = GetSorted("8, 1, 3, 7"); // Results in int32[4](1, 3, 7, 8)
// Create serialization method at compile-time on types with [Serialize] // No runtime reflection required [AttributeUsage(.Types)] struct SerializableAttribute : Attribute, IComptimeTypeApply { [Comptime] public void ApplyToType(Type type) { let code = scope String(); code.Append("void ISerializable.Serialize(SerializationContext ctx)\n{"); for (let field in type.GetFields()) code.AppendF($"\n\tctx.Serialize(\"{field.Name}\", {field.Name});"); code.Append($"\n}"); Compiler.EmitAddInterface(type, typeof(ISerializable)); Compiler.EmitTypeBody(type, code); } }

Custom IDE

The Beef Development Tools include an IDE with a general-purpose debugger capable of debugging native applications written in any language.

Productivity Features

The IDE supports productivity features such as autocomplete, fixits, reformatting, refactoring tools, type inspection, hot compilation, and a built-in profiler.

Mixed Builds

Beef allows for safely mixing different optimization levels on a per-type or per-method level, allowing for performance-critical code to be executed at maximum speed without affecting debuggability of the rest of the application.

Real-time Leak Detection

Beef can detect memory leaks in real-time. As with most safety features in Beef, this can be turned off in Release builds for performance-critical applications.

Read Entire Article