HN 展示:Micron – 具备 C 语言能力的 Oberon
Show HN: Micron – Oberon with the Power of C

原始链接: https://github.com/rochus-keller/Micron

Micron 是一种系统编程语言,结合了 Oberon 的模块化与严格性以及 C 的灵活性,支持引导代码、驱动程序、内核、应用程序和直接 C 调用。它使用 0–5 级语言级别来控制可用特性并防止不安全依赖。项目包含编译器、解释器和 IDE,并提供许多迁移的基准测试、操作系统示例和一本教科书。Micron 支持泛型、内联、编译时过程和 Pascal 风格运算符;保留字存在小写和大写形式。编译器和解释器已就绪,而原生后端和 C 转译器仍在进行中;后端大多基于栈。它可使用 LeanQt/BUSY 或 Qt 5 构建,并提供预编译系统。Micron 采用 GPL 2/3 许可,运行时和 Oakwood 库也可在 MPL 或 LGPL 下获得。

Micron(“MicroOberon”)是 Rochus Keller 开发的一种精简、静态类型、模块化的系统语言,它融合了 Oberon 的简洁性与 C 的底层能力。它旨在支持引导代码、驱动程序、内核和应用程序,不使用垃圾回收器或不安全的系统方言,提供显式内存布局、有符号/无符号整数、指针操作和直接 C 调用。其“语言级别”允许模块声明所需能力,并仅导入同等或更低级别的模块,防止意外依赖(如堆分配)。其他特性包括泛型、结构化接口和编译时过程。编译器、解释器、IDE 和教科书均已存在;示例包括 Smalltalk-80、Scheme 9、P4 Pascal 和 Project Oberon 的移植,包括一个 RISC-V 系统。原生后端仍在进行中。评论者称赞了全面的工作和示例,讨论了 Oberon 的传承,并就 Pascal 风格的顶层声明和单遍编译速度进行了辩论。
相关文章

原文

Logo

This project implements a compiler, interpreter and IDE for the Micron programming language as specified here; see also the code examples below.

Micron is a systems programming language with a syntax similar to Oberon+ and the reach of C. The name "Micron" is an abbreviation of "MicroOberon".

Micron is intended as a practical alternative to C for systems programming. It combines the modularization, strictness and simplicity of Oberon with the flexibility of C: the same language serves for boot code, drivers, an operating system kernel and ordinary applications, and it calls C without wrappers. The preprocessor is replaced by language concepts that can largely substitute its features, supplemented by explicit support for generic programming, inlining and procedures that run at compile time. Thereby the focus is always on simplicity and the greatest effect with a practicable effort.

Micron supports language levels: a module declares the level it needs, from level 0 (inlined code for hardware initialization, no stack) over level 1 (stack frames, recursion, extern C procedures), level 3 (heap, pointers, exceptions) and level 4 (type extension and dynamic dispatch) up to level 5 (garbage collection). A module can only import modules of the same or a lower level, so a kernel written at level 1 cannot depend on the heap by accident. Without the attribute, level 3 is assumed.

In addition to the specification, a free textbook is available which shows how each feature of Micron is used and why it is designed the way it is. Part III walks through the Micron System, a port of Wirth and Gutknecht's Project Oberon operating system, which is compiled, linked and booted on an emulated RISC-V machine with a single command.

Example applications and systems

Since the start of the project in January 2024, numerous benchmarks, virtual machines and operating systems of significant size have been migrated to Micron. The following references comprise about 130 kSLOC of working Micron code.

The examples subdirectory contains:

  • a Smalltalk-80 implementation,
  • the original 1976 P4 Pascal compiler,
  • the Scheme 9 virtual machine,
  • the Project Oberon System running on the host,
  • and the Micron System described in the textbook, the migrated Project Oberon System natively running on RISC-V.

The Are-we-fast-yet repository includes the same benchmark suite in four different implementations for comparison: procedural, hybrid, interface-based and object-oriented.

The migration of the Oberon System 3 to Micron currently runs on the emulated RISC-V machine; support for the Raspberry Pi Zero 2 is work in progress.

The following precompiled versions are available at this time:

Just download, unpack and run. The command line options are described in the micc manual.

Here is a precompiled version of the Micron System discussed in the book which you can run on Linux x86_64 by just typing ./rv32vm --elf --ram 4 --disk disk.img system.bin (requires SDL2). More information is in the Readme.md.

Micron is case sensitive, but each reserved word and predeclared identifier exists in a fully lower-case and a fully upper-case form with identical meaning; the last example is written in the Oberon style, all others in the lower-case style used in the book. Statement separators are optional.

Low-level programming and C interoperability
module Bits [level = 1]

var
    a: array 4 of int32
    p: ^int32
    u, u1: uint32

begin
    println(band(0F0FH, 00FFH))    // 15
    println(bor(1, 4))             // 5
    println(bxor(5, 1))            // 4
    println(shl(1, 5))             // 32
    println(shr(0FFH, 4))          // 15, logical
    println(asr(-16, 2))           // -4, arithmetic

    u1 := 0FFFFFFFFH
    println(cast(int32, u1))       // -1, same bits
    u := usig(-1)
    println(u)                     // 4294967295, same bits

    a[0] := 10; a[1] := 20; a[2] := 30
    p := @a[0]
    p := ptroff(p, 1)              // advance by one int32
    println(p^)                    // 20
    inc(p)
    println(p^)                    // 30
end Bits

The file is in the examples subdirectory and compiles/runs with ./micc Bits.mic --run.

module Clib [level = 1]

procedure abs(x: int32): int32; extern C
procedure toupper(c: int32): int32; extern C
procedure write(fd: int32; const buf: ^[]char; n: uint32): int32
    extern C "write"

var s: array 3 of char

begin
    s := "ok"
    if write(1, @s, 2) # 2 then return end
    println(abs(-5))
    println(toupper(sigl(ord('a'))))    // 65
end Clib
Abstraction without objects
module Shapes [level = 2]

type
    Shape = interface                   // a contract, satisfied structurally
                proc area(): integer
                proc print()
            end

    Rect   = record w, h: integer end
    Circle = record radius: integer end

procedure Rect.area(): integer          // type-bound procedure, bound statically
begin
    return self.w * self.h
end Rect.area

procedure Rect.print()
begin
    print("rect "); print(self.w); print("x"); print(self.h)
end Rect.print

procedure Circle.area(): integer
begin
    return 3 * self.radius * self.radius    // coarse
end Circle.area

procedure Circle.print()
begin
    print("circle r="); print(self.radius)
end Circle.print

procedure describe(s: Shape)            // accepts any type with area and print
begin
    s.print(); print(" area="); println(s.area())
end describe

var
    r: Rect
    c: Circle

begin
    r.w := 4; r.h := 3
    c.radius := 5
    describe(@r)                        // rect 4x3 area=12
    describe(@c)                        // circle r=5 area=75
end Shapes
module Stack(T; const CAP: uint32)
var
    a: array CAP of T
    n*: integer

procedure push*(x: T)
begin
    a[n] := x; inc(n)
end push

procedure pop*(): T
begin
    dec(n)
    return a[n]
end pop

begin
    n := 0
end Stack

module UseStack
import si := Stack(integer, 10)
    cs := Stack(char, 4)
begin
    si.push(3); si.push(4)
    cs.push('x')
    println(si.pop() + si.pop())    // 7
    println(cs.pop())               // x
end UseStack
module Comptime

const
    KB      = 1024
    BUFSIZE = getenv(uint32, "BUFSIZE", 16 * KB)   // micc -D BUFSIZE=...
    WORDS   = BUFSIZE div size(integer)
    DEBUG   = getenv(boolean, "DEBUG")             // false unless -D DEBUG

procedure width(n: uint32): uint32 invar   // callable at compile time
begin
    if n < 2 then return 1 else return 1 + width(n div 2) end
end width

const
    INDEXBITS = width(WORDS - 1)           // 12, computed by the compiler

var
    buf: array WORDS of integer
    index: array INDEXBITS of boolean

begin
    println(len(buf))       // 4096
    println(len(index))     // 12
    <* if DEBUG then *>
    println("debug build")
    <* else *>
    println("release build")
    <* end *>
end Comptime
MODULE Sorter;

PROCEDURE Sort*(a: ^[]INTEGER; n: UINT32);
    VAR i, j, t: INTEGER;
BEGIN
    FOR i := 1 TO n-1 DO
        t := a[i]; j := i-1;
        WHILE (j >= 0) & (a[j] > t) DO
            a[j+1] := a[j]; DEC(j)
        END;
        a[j+1] := t
    END
END Sort;

END Sorter.

MODULE Main;
    IMPORT Sorter;
    VAR a: ARRAY 8 OF INTEGER; i: INTEGER;
BEGIN
    a := { 5, 3, 8, 1, 9, 2, 7, 4 };
    Sorter.Sort(@a, LEN(a));
    FOR i := 0 TO LEN(a)-1 DO PRINT(a[i]); PRINT(" ") END;
    PRINTLN("")
END Main.

To make the transition from Pascal easier, Micron supports Pascal operators like <>, and or not (see e.g. the P4 compiler).

The compiler and interpreter are implemented and up-to-date with the specification. Development continues on native backends and the C transpiler (pending pcall, raise, finally), IDE integration, tooling and additional system ports. Also note that the current backends are mostly stack based for simplicity (and expect that there are still bugs). Register based implementations were considered but postponed to keep the first version of the backends simple and easy to verify. The textbook explains why neither Wirth's compilers nor mine so far include any global optimization infrastructure.

Micron IDE

Follow these steps if you want to build the Micron compiler yourself. The build is using LeanQt and the BUSY build system. Note that compiling on Linux requires the build essentials. On Mac and Windows there are no additional requirements than a toolchain.

  1. Create a new directory; we call it the root directory here
  2. Download https://github.com/rochus-keller/micron/archive/refs/heads/master.zip and unpack it to the root directory; rename the resulting directory to "Micron".
  3. Download https://github.com/rochus-keller/LeanQt/archive/refs/heads/master.zip and unpack it to the root directory; rename the resulting directory to "LeanQt".
  4. Download https://github.com/rochus-keller/BUSY/archive/refs/heads/master.zip and unpack it to the root directory; rename the resulting directory to "build".
  5. Open a command line in the "build" directory and type cc *.c -O2 -lm -o lua or cl /O2 /MD /Fe:lua.exe *.c depending on whether you are on a Unix or Windows machine; wait a few seconds until the Lua executable is built.
  6. Now type ./lua build.lua ../Micron (or lua build.lua ../Micron on Windows); wait until the micc executable is built; you find it in the output subdirectory.

Instead of the command line you can run the build using LeanCreator which uses multiple cores and thus builds faster.

It is still possible to build the compiler using Qt 5 with qmake; use MicMain.pro for this purpose and proceed as usual when building with Qt.

The IDE can be built using MicIde.pro with qmake. BUSY IDE support is pending.

If you need support or would like to post issues or feature requests please use the Github issue list at https://github.com/rochus-keller/micron/issues or send an email to the author.

Micron is available under GPL 2 or 3. The runtime and oakwood libraries in the corresponding subfolders, in addition, are available under MPL or LGPL.

联系我们 contact @ memedata.com