proj-plbook-plChCtmLangs

Table of Contents for Programming Languages: a survey

C

Because it is so well-known and well-liked, C gets its own chapter.

See chapter on chapter on C.

C++

"In C++’s case, the Big Idea was high level object orientation without loss of performance compared to C." [1] (my note: and backwards compatibility with C; and close-to-the-metal low-level operation)

" C++ is a general-purpose programming language with a bias towards systems programming that

    is a better C
    supports data abstraction
    supports object-oriented programming
    supports generic programming " -- http://www.stroustrup.com/C++11FAQ.html#aims

" ...C++’s fundamental strengths:

Attributes:

Pros:

Cons:

Good for:

Best practices and style guides:

References:

Retrospectives:

People:

Tours and tutorials:

Reference/details:

Books:

Opinions:

"With C++ I at least have the escape hatch of going back to C. I started using some of the C++ 11, 14, 17 template and compile time programming stuff: constexpr, etc. Some of it is OK. There are lots of holes. When there’s a hole, I go back to a C macro! That has worked well enough many times. So ironically C++ has incredible complexity, but you can always avoid it by programming in C :) On the other hand, the template stuff can be useful. I kinda cringe when I realize someone’s first experience with systems programming could be in Rust. But then again I also cringe at how long it took to be productive in C and C++ too (more than a decade). We’re not there yet :) " -- [10]

on C++ interop:

Comparisons:

Examples of good code:

Best practices:

Misc:

Types:

C++ Features

References

References are kind of like pointers, but with a few differences. Some of these are:

For example, say you want to write a '++' operator for a type that doesn't have one. You have to use references for two reasons:

Links:

C++ Features: Concepts

discussion (with comparison to Rust trait syntax and semantics):

logicchains 1 day ago

parent prev next [–]

>That said, I've mostly reached the conclusion that much of this is unavoidable. Systems languages need to have lots of detail you just don't need in higher level languages like Haskell or Python, and trait impls on arbitrary types after the fact is very powerful and not something I would want to give up.

Have you checked out C++20 concepts? It supports aliases and doesn't require explicit trait instantiations, making it possible to right such generic code with much less boilerplate.

reply

loeg 1 day ago

root parent next [–]

My experience in C++ prior to 20 is that it is a lot more verbose/boilerplatey than Rust. I'd love to see that get better, but I think C++ is starting from significantly behind.

reply

jcelerier 1 day ago

root parent next [–]

C++17:

    template<typename T, std::enable_if_t<std::is_floating_point_v<T>, int>* = nullptr>
    void func(T fp) { ... }

C++20:

    void func(std::floating_point auto fp) { ... }

reply

Frizi 1 day ago

root parent next [–]

There is an equivalent syntax in Rust to both of those examples, and in both cases I find it less verbose. The template variant is roughly equivalent to:

    fn func<T: FloatingPoint>(fp: T) { ... }

And the "auto" variant is similar to impl argument in Rust:

    fn func(fp: impl FloatingPoint) { ... }

reply

jcelerier 1 day ago

root parent next [–]

I really don't see in which way the second case is less verbose especially if you add a non-void return type, e.g. i32. The first case would also be doable like this, which is pretty munch the exact same than your first example with the added "template" keyword

    template<std::floating_point T> 
    void func(T t) { ... }

(also, it's not really equivalent - if I'm not mistaken with traits you can only use what the trait declares ; in C++ you can for instance do something like

    template<typename T>
    concept CanBlah = requires (T t) { 
      t.blah();
    };

and still be able to do

    void func(CanBlah auto t) { 
      log << "func:" << t;
      t.blah();
    }

instead of polluting the prototype with all possible side concerns)

reply

tialaramex 1 day ago

root parent next [–]

> instead of polluting the prototype with all possible side concerns)

C++ Concepts are duck typed, and Rust's Traits are not, so in Rust you are expressing meaning here, and in C++ only making some claims about syntax which perhaps hint at meaning.

WG21 seems to dearly wish this wasn't so, offering Concepts which pretend to semantics they don't have, such as std::totally_ordered and I'm glad to see your "CanBlah?" concept doesn't do this, to be sure all things which match this requirement can, indeed blah() although we've no idea what that can or should do.

Once you've accepted that you only have duck typing anyway, you're probably going to have to explain in your documentation the actual requirements for this parameter t, as the prototype merely says it CanBlah? and that's not actually what we care about.

In contrast the Rust function we looked at actually does tell us what is required here, something which "implements FloatingPoint?", and that implementation (plus the data structure itself) is all that's being exposed.

reply

C++ features: coroutines

C++ Opinions

Favorite parts:

Critical:

Other:

    def adder(amount):
        return lambda x: x + amount

C++

    auto adder(int amount) {
        return [=](int x){ return x + amount; };
    }" about the [=]: "turns out there are various forms of Lambda (of course there wouldn't just be one in C++), for ex:
    [a, &b], [this], [=], [&], []

Depending on how the variables are captured. " "The stuff in square brackets is the capture list. Unlike python, which implicitly captures the full outer scope by reference, C++ allows/requires you to specify which outer variables you want to reference in the lambda, and whether they are captured by value ("=") or reference ("&"). This allows C++ to implement the lambda with a simple static function in most cases, where python needs a bunch of tracking data to preserve the outer stack from collection." 00 [35]

Why it compiles slow:

C++ advanced practices

C++ Gotchas

"the major insecurities in C++ code:

C++ Popular libraries outside of stdlib

C++ tools

Linters:

Formatters:

C++ variants

D

D Tutorials

D Pros

D Retrospectives

D features

Safety features

"D doesn't allow implicit narrowing conversions. The user has to have an explicit cast, like `cast(int) size`. Cast is made into a keyword so all those explicit conversions can be found with a simple grep." Walter Bright

D Opinions

Opinionated comparisons:

D variants and implementations

BetterC variant

runtimeless restricted variant of D, "BetterC?":

https://dlang.org/spec/betterc.html

https://dlang.org/blog/2017/08/23/d-as-a-better-c/

" Retained Features

Nearly the full language remains available. Highlights include:

    Unrestricted use of compile-time features
    Full metaprogramming facilities
    Nested functions, nested structs, delegates and lambdas
    Member functions, constructors, destructors, operating overloading, etc.
    The full module system
    Array slicing, and array bounds checking
    RAII (yes, it can work without exceptions)
    scope(exit)
    Memory safety protections
    Interfacing with C++
    COM classes and C++ classes
    assert failures are directed to the C runtime library
    switch with strings
    final switch
    unittest

...

Unavailable Features

D features not available with BetterC?:

    Garbage Collection
    TypeInfo and ModuleInfo
    Classes
    Built-in threading (e.g. core.thread)
    Dynamic arrays (though slices of static arrays work) and associative arrays
    Exceptions
    synchronized and core.sync
    Static module constructors or destructors

"

"What may be initially most important to C programmers is memory safety in the form of array overflow checking, no more stray pointers into expired stack frames, and guaranteed initialization of locals. This is followed by what is expected in a modern language — modules, function overloading, constructors, member functions, Unicode, nested functions, dynamic closures, Compile Time Function Execution, automated documentation generation, highly advanced metaprogramming, and Design by Introspection." [70]

LWDR - Light Weight D Runtime

https://github.com/hmmdyl/LWDR

" What is this?

This is the light weight D runtime - it is a barebones runtime targeting ARM Cortex CPUs. It works by abstracting hooks that the user can connect to their selected backend (be it an RTOS such as FreeRTOS?, ChibiOS?, etc or a minimalist system). What works?

    Class allocations and deallocations (via new and delete)
    Struct heap allocations and deallocations (via new and delete)
    Invariants
    Asserts
    Contract programming
    Basic RTTI (via TypeInfo stubs)
    Interfaces
    Static Arrays
    Virtual functions and overrides
    Abstract classes
    Static classes
    Allocation and deallocation of dynamic arrays (opt in by version LWDR_DynamicArray)
    Concatenate an item to a dynamic array (opt in by version LWDR_DynamicArray)
    Concatenate two dynamic arrays together (opt in by version LWDR_DynamicArray)
    Dynamic array resizing (opt in by version LWDR_DynamicArray)
    Thread local storage (opt in by version LWDR_TLS)

What doesn't work?

    Exceptions and Throwables (experimental implementation was removed)
    Module constructors and destructors
    Static constructors and destructors
    Shared static constructors and destructors
    Module info
    There is no GC implementation (primitive memory tracking is now available with LWDR_TrackMem, RefCount!T and Unique!T are now available)
    Delegates/closures
    Associative arrays
    Object monitors
    Shared/synchronised" [71]

Rust

Rust has its own chapter; see [[plChRust?]].

Objective-C

Attributes:

Pros:

Used in:

tutorials:

overviews:

Feature overviews:

Best practices and style guides:

Versions:

Opinions:


Less popular close-to-the-metal languages

Nim

http://nim-lang.org/

Formerly Nimrod.

" Nim (formerly known as "Nimrod") is a statically typed, imperative programming language that tries to give the programmer ultimate power without compromises on runtime efficiency. This means it focuses on compile-time mechanisms in all their various forms.

Beneath a nice infix/indentation based syntax with a powerful (AST based, hygienic) macro system lies a semantic model that supports a soft realtime GC on thread local heaps. Asynchronous message passing is used between threads, so no "stop the world" mechanism is necessary. An unsafe shared memory heap is also provided for the increased efficiency that results from that model. "

" you could use Nim[1] which compiles to C. If you, like myself, dislike C, Nim can be used as a "better C" (you can disable the Nim GC and forget about its standard library, then use Nim as a thin layer over C with modules, generics and other nice goodies) " [74]

Tutorials

Opinions and comparisons

Nim Retrospectives

Nim features

Nim syntax

" Nim distinguishes between statements and expressions and most of the time an expression is a function application (also called a “procedure call”). Function application uses the traditional mathy syntax with the parentheses: f(), f(a), f(a, b).

And here is the sugar: Sugar Meaning Example 1 f a f(a) spawn log("some message") 2 f a, b f(a, b) echo "hello ", "world" 3 a.f() f(a) db.fetchRow() 4 a.f f(a) mystring.len 5 a.f(b) f(a, b) myarray.map(f) 6 a.f b f(a, b) db.fetchRow 1 7 f"\n" f(r"\n") re"\b[a-z*]\b" 8 f a: b f(a, b) lock x: echo "hi"

    In rules 1 and 2 you can leave out the parentheses and there are examples so that you can see why that might be useful: spawn looks like a keyword, which is good, since it does something special; echo is also famous for leaving out the parentheses because usually you write these statements for debugging, thus you are in a hurry to get things done.
    You have a dot notation available and you can leave out parentheses (rules 3-6).
    Rule 7 is about string literals: f followed by a string without whitespace is still a call but the string is turned into a raw string literal, which is very handy for regular expressions because regular expressions have their own idea of what a backslash is supposed to mean.
    Finally, in the last rule we can see that you can pass a block of code to the f with a : syntax. The code block is usually the last argument you pass to the function. This can be used to create a custom lock statement.

There is one exception to leaving out the parentheses, in the case you want to refer to f directly: f does not mean f(). In the case of myarray.map(f) you do not want to invoke f, instead you want to pass the f itself to map. " -- [82]

PLC languages

There are a few standard languages for Programmable Logic Controllers (a type of simple processor used for industrial automation). The relevant standard is ISO 61131. The languages are:

Links:

Quaint

Quaint is a small, clean descendent of C with first-class resumable functions "The main goal of Quaint is to demonstrate the viability of a new idiom for "sane" concurrent programming that bears resemblance to promises and generators". It is implemented via a VM ('QVM').

Some things that it attempts to do better than C:

Features

Concurrency model:

" In essence, every function can be called either synchronously (just as in C) or asynchronously via a quaint ((promise)) object. The called function does not have any explicit "yield" statements, but merely only optional "wait labels" that mark eventual suspension points in which the caller might be interested in. The caller decides whether to wait with a timeout or wait until reaching a certain "wait label". The caller can also query the quaint to see whether it has lastly passed a particular wait label. The callee can have optional noint blocks of statements during which it cannot be interrupted even if the caller is waiting with a timeout and that timeout has elapsed.

Quaint is entirely single-threaded – asynchronous functions run only when the caller is blocked on a wait statement or on a "run-till-end & reap-value" operator. During all other times, the resumable function is in a "frozen" state which holds its execution context and stack. "

-- https://github.com/bbu/quaint-lang

Three loop statements:

Variable declaration syntax: eg "a: byte = 0;". Note: ':' is the 'typecast' operator.

eg

a: int; // int a
p: ptr(int); // int *p
q: ptr(ptr(long)); // long **q
arr: int[20]; // int arr[20]
arrp: ptr[20](int); // int *arrp[20]
parr: ptr(int[20]); // int (*parr)[20]
fp: fptr(a: int, b: int): long; // long (*fp)(int a, int b)

Function syntax example:

f1(a: uint, b: uint): ulong
{
    const addend: ulong = 5:ulong;
    return a:ulong + b:ulong + addend;
}

" User-defined types can be defined at file scope via the type statement:

type mytype: struct(
    member_one: ptr(quaint(byte)),
    member_two: vptr,
    member_three: u32,
    member_four: fptr(x: int[12]): u64
);

"

Types:

Operators:

binary:

AND, OR
^ (bitwise AND, OR, XOR)

unary:

Built-in functions:

i)(8163264), pnl

Concurrency model:

Promises ("quaints"). A promise can be null. The type of a promise contains its return value and is written eg 'quaint(int)' (a promise with no return is of type 'quaint()'). A promise is initialized from a function invocation expression with the '~' operator; this creates it but does not start/run it. If '~' is applied to a value rather than a function, it creates a (concepually) 0-ary function returning that value.

The '@' query operator takes a promise and a label and returns 1 if the promise has passed that label, or 0 otherwise. The label reference syntax is eg 'my_function::a_label'; the @ syntax is eg 'q@my_function::a_label'. @ applied to a null promise returns 0.

A label has the syntax eg '[label]'. Duplicate wait labels are allowed in a function; if either of them is passed, the label 'fires'. There are two special built-in labels: 'start' and 'end'.

The 'wait' operator runs a promise, suspending the caller (everything is single-threaded here). Optionally, 'wait' can suspend the promise and return control to the caller when a condition is met; the condition can either be a timeout, or whether the promise has passed a label (see also '@', above), or whether the promise is itself waiting on something marked 'blocking' (not yet implemented). Wait does not return anything; it only has side-effects.

The '*' operator 'reaps' a promise, deallocating it and returning its return value, after first 'waiting' until it completes. "This operator can be thought of as a counterpart of the ~ quantify operator. The former allocates the quaint, the latter releases it. The VM does not release any orphaned quaints, so a failure to release the quaint via * causes a memory leak in your program." "When applied over a null quaint, * returns a zero value of the appropriate subtype or nothing in case of a void subtype."

'noint' (nointerrupt) blocks (noint {...}) may be used to indicate that some portion of callee code may not be preempted (suspended due to the condition of the caller's 'wait' being met). This is similar to a 'critical section' (although Quaint is single-threaded). "This is usually useful when modifying some global state variables which may be shared with the caller:"

Currently lacking features compared to C (almost a direct quote from the README):

Future directions:

"

Anti-features (things that will NOT be added):

Implementation details

QVM virtual machine main addressing modes (from [84]): "

QVM virtual machine instruction set (from https://github.com/bbu/quaint-lang/blob/master/src/codegen.h ):

AST

AST node types (from https://github.com/bbu/quaint-lang/blob/master/src/ast.h ):

Zig

http://ziglang.org/

"

    Manual memory management. Memory allocation failure is handled correctly. Edge cases matter!
    Zig competes with C instead of depending on it. The Zig Standard Library does not depend on libc.
    Small, simple language. Focus on debugging your application rather than debugging your knowledge of your programming language.
    A fresh take on error handling that resembles what well-written C error handling looks like, minus the boilerplate and verbosity.
    Debug mode optimizes for fast compilation time and crashing with a stack trace when undefined behavior would happen.
    ReleaseFast mode produces heavily optimized code. What other projects call "Link Time Optimization" Zig does automatically.
    ReleaseSafe mode produces optimized code but keeps safety checks enabled. Disable safety checks in the bottlenecks of your code.
    Generic data structures and functions.
    Compile-time reflection and compile-time code execution.
    Import .h files and directly use C types, variables, and functions.
    Export functions, variables, and types for C code to depend on. Automatically generate .h files.
    Nullable type instead of null pointers.
    Order independent top level declarations.
    Friendly toward package maintainers. Reproducible build, bootstrapping process carefully documented. Issues filed by package maintainers are considered especially important.
    Cross-compiling is a first-class use case.
    No preprocessor. Instead Zig has a few carefully designed features that provide a way to accomplish things you might do with a preprocessor."

Zig Tutorials

Zig Features

" Why Zig When There is Already CPP, D, and Rust?

No hidden control flow

If Zig code doesn't look like it's jumping away to call a function, then it isn't. This means you can be sure that the following code calls only foo() and then bar(), and this is guaranteed without needing to know the types of anything:

var a = b + c.d; foo(); bar();

...

No hidden allocations

More generally, have a hands-off approach when it comes to heap allocation. There is no new keyword or any other language feature that uses a heap allocator (e.g. string concatenation operator[1]).

...

First-class support for no standard library

Zig has an entirely optional standard library that only gets compiled into your program if you use it. Zig has equal support for either linking against libc or not linking against it ...

A Portable Language for Libraries ...

A Package Manager and Build System for Existing Projects ...

Simplicity Zig has no macros and no metaprogramming, yet still is powerful enough to express complex programs in a clear, non-repetitive way. Even Rust which has macros special cases fmt.Print!, rather than just a simple function. Meanwhile in Zig, the equivalent function is implemented in the standard library with no sort of meta programming/macros.

When you look at Zig code, everything is a simple expression or a function call. There is no operator overloading, property methods, runtime dispatch, macros, or hidden control flow. Zig is going for all the beautiful simplicity of C, minus the pitfalls. " -- https://github.com/ziglang/zig/wiki/Why-Zig-When-There-is-Already-CPP,-D,-and-Rust%3F

" Compile-time parameters is how Zig implements generics. It is compile-time duck typing.

fn max(comptime T: type, a: T, b: T) T { return if (a > b) a else b; } fn gimmeTheBiggerFloat(a: f32, b: f32) f32 { return max(f32, a, b); } fn gimmeTheBiggerInteger(a: u64, b: u64) u64 { return max(u64, a, b); }

In Zig, types are first-class citizens. They can be assigned to variables, passed as parameters to functions, and returned from functions. However, they can only be used in expressions which are known at compile-time, which is why the parameter T in the above snippet must be marked with comptime. " -- [91]

"The new test allocator checks your code for memory leaks, use after free, and double free in a highly ergonomic fashion." -- [92]

" Thus it is actually useful to clarify all the things Zig doesn’t have to show how small it is:

    No class inheritance like in C++, Java, Swift etc.
    No runtime polymorphism through interfaces like Go.
    No generics. You cannot specify generic interfaces like in Swift checked at compile time.
    No function overloading. You cannot write a function with the same name multiple times taking different arguments.
    No exception throwing.
    No closures.
    No garbage collection.
    No constructors and destructors for use with Resource acquisition is initialization (RAII).

However Zig is able to provide much the same functionality by clever use of a few core features:

    Types can be passed around like objects at compile time.
    Tagged Unions. Also called sum types or variants in other programming languages.
    Function pointers.
    Real pointers and pointer arithmetic.
    Zig code can be partially evaluated at compile time. You can mark code to be executable at compile time, with comptime keyword.
    Functions and types can be associated with structs, but are not physically stored in structs, so invisible to C code." [93]

Zig Opinions:

Zig opinionated comparisons:

link flag

Zig gotchas:

Zig discussions

Zig internals

---

Virgil

http://compilers.cs.ucla.edu/virgil/overview.html

Types:

" Virgil provides three basic primitive types. These primitive types are value types, and quantities of these types are never passed by reference.

    int - a signed, 32-bit integer type with arithmetic operations
    char - an 8-bit quantity for representing ASCII characters
    boolean - a true / false type for representing conditions

Additionally, Virgil provides the array type constructor [] that can construct the array type T[] from any type T. ... unlike Java, Virgil arrays are not objects, and are not covariantly typed [1]. "

OOP: " Virgil is a class-based language that is most closely related to Java, C++, and C#. Like Java, Virgil provides single inheritance between classes "

" Compile-time Initialization

The most significant feature of Virgil that is not in other mainstream languages is the concept of initialization time. To avoid the need for a large runtime system that dynamically manages heap memory and performs garbage collection, Virgil does not allow applications to allocate memory from the heap at runtime. Instead, the Virgil compiler allows the application to run initialization routines at compilation time, while the program is being compiled. ... When the application's initialization routines terminate... The reachable heap is then compiled directly into the program binary and is immediately available to the program at runtime. "

first-class functions ('delegates'):

" Delegates

which is a first-class value that represents a reference to a method. A delegate in Virgil may be bound to a component method or to an instance method of a particular object; either kind can be used interchangeably, provided the argument and return types match. Delegate types in Virgil are declared using the function type constructor. For example, function(int): int represents the type of a function that takes a single integer as an argument and returns an integer. ... Unlike C#, the use of delegate values, either creation or application, does not require allocating memory from the heap. Delegates are implemented as a tuple of a pointer to the bound object and a pointer to the delegate's code.

on implicit narrowing conversions: " I thought about this very, very carefully when designing Virgil[1]'s numerical tower, which has both fixed-size signed and unsigned integers, as well as floating point. Like other new language designs, Virgil doesn't have any implicit narrowing conversions (even between float and int). Also, any conversions between numbers include range/representability checks that will throw if out-of-range or rounding occurs. If you want to reinterpret the bits, then there's an operator to view the bits. But conversions that have to do with "numbers" then all make sense in that numbers then exist on a single number line and have different representations in different types. Conversion between always preserve numbers and where they lie on the number line, whereas "view" is a bit-level operation, which generally compiles to a no-op. Unfortunately, the implications of this for floating point is that -0 is not actually an integer, so you can't cast it to an int. You must round it. But that's fine, because you always want to round floats to int, never cast them. " [132]

---

Crystal

Opinions:

---

Pony

Opinionated comparisons:

---

Mu (akkartik's)

"basically a cleaned up reimplementation of C's memory model, with the ability to manipulate addresses in memory." -- [137]

https://github.com/akkartik/mu/blob/main/mu.md

http://akkartik.name/post/mu-2019-2

(note: in that link at one place he speaks of allocating local variables on the stack, but in http://akkartik.name/coroutines-in-mu/ it is clear that activation records are only on the heap)

http://akkartik.name/akkartik-convivial-20200607.pdf

http://akkartik.name/coroutines-in-mu/

Much of the following is copied from https://github.com/akkartik/mu/blob/main/mu.md :

" If you learned programming using C you probably learned to create a generic linked list something like this (using a sort of timeless mish-mash of C and C++ that is guaranteed to not compile):

 struct list<T> {
   T value;
   list<T>* rest;
 }

In Mu you'd write this as:

 container list:_T [
   value:_T
   rest:address:list:_T  # "an address to a list of _T"
 ]" -- [138]

Other available stuff (from https://github.com/akkartik/mu/blob/main/vocabulary.md ):

    offset 0: write index
    offset 4: read index
    offset 8: size of array (in bytes)
    offset 12: start of array data
    Invariant: 0 <= read <= write <= size

Syscalls:

"primitives built atop system calls" (standard library):

(Compound arguments are usually passed in by reference. Where the results are compound objects that don't fit in a register, the caller usually passes in allocated memory for it.)

"

(Compound arguments are usually passed in by reference. Where the results are compound objects that don't fit in a register, the caller usually passes in allocated memory for it.) assertions for tests

    check-ints-equal: fails current test if given ints aren't equal
    check-stream-equal: fails current test if stream doesn't match string
    check-next-stream-line-equal: fails current test if next line of stream until newline doesn't match string

error handling

    error: takes three arguments, an exit-descriptor, a file and a string (message)
    Prints out the message to the file and then exits using the provided exit-descriptor.
    error-byte: like error but takes an extra byte value that it prints out at the end of the message.

predicates

    kernel-string-equal?: compares a kernel string with a string
    string-equal?: compares two strings
    stream-data-equal?: compares a stream with a string
    next-stream-line-equal?: compares with string the next line in a stream, from read index to newline
    slice-empty?: checks if the start and end of a slice are equal
    slice-equal?: compares a slice with a string
    slice-starts-with?: compares the start of a slice with a string
    slice-ends-with?: compares the end of a slice with a string

writing to disk

    write: string -> file
        Can also be used to cat a string into a stream.
        Will abort the entire program if destination is a stream and doesn't have enough room.
    write-stream: stream -> file
        Can also be used to cat one stream into another.
        Will abort the entire program if destination is a stream and doesn't have enough room.
    write-slice: slice -> stream
        Will abort the entire program if there isn't enough room in the destination stream.
    append-byte: int -> stream
        Will abort the entire program if there isn't enough room in the destination stream.
    append-byte-hex: int -> stream
        textual representation in hex, no '0x' prefix
        Will abort the entire program if there isn't enough room in the destination stream.
    print-int32: int -> stream
        textual representation in hex, including '0x' prefix
        Will abort the entire program if there isn't enough room in the destination stream.
    write-buffered: string -> buffered-file
    write-slice-buffered: slice -> buffered-file
    flush: buffered-file
    write-byte-buffered: int -> buffered-file
    print-byte-buffered: int -> buffered-file
        textual representation in hex, no '0x' prefix
    print-int32-buffered: int -> buffered-file
        textual representation in hex, including '0x' prefix

reading from disk

    read: file -> stream
        Can also be used to cat one stream into another.
        Will silently stop reading when destination runs out of space.
    read-byte-buffered: buffered-file -> byte
    read-line-buffered: buffered-file -> stream
        Will abort the entire program if there isn't enough room.

non-IO operations on streams

    new-stream: allocates space for a stream of n elements, each occupying b bytes.
        Will abort the entire program if n*b requires more than 32 bits.
    clear-stream: resets everything in the stream to 0 (except its size).
    rewind-stream: resets the read index of the stream to 0 without modifying its contents.

reading/writing hex representations of integers

    is-hex-int?: takes a slice argument, returns boolean result in eax
    parse-hex-int: takes a slice argument, returns int result in eax
    is-hex-digit?: takes a 32-bit word containing a single byte, returns boolean result in eax.
    from-hex-char: takes a hexadecimal digit character in eax, returns its numeric value in eax
    to-hex-char: takes a single-digit numeric value in eax, returns its corresponding hexadecimal character in eax

tokenization

from a stream:

    next-token: stream, delimiter byte -> slice
    skip-chars-matching: stream, delimiter byte
    skip-chars-not-matching: stream, delimiter byte

from a slice:

    next-token-from-slice: start, end, delimiter byte -> slice
        Given a slice and a delimiter byte, returns a new slice inside the input that ends at the delimiter byte.
    skip-chars-matching-in-slice: curr, end, delimiter byte -> new-curr (in eax)
    skip-chars-not-matching-in-slice: curr, end, delimiter byte -> new-curr (in eax)"

Links:

Odin

https://odin-lang.org/

Forth

See [[plChContatenativeLangs.txt?]]

PICL

http://people.inf.ethz.ch/wirth/PICL/index.html http://people.inf.ethz.ch/wirth/PICL/PIC.pdf http://people.inf.ethz.ch/wirth/PICL/PICLcompiler.pdf

Hare

https://harelang.org/blog/2022-04-25-announcing-hare/

Hare Retrospectives

Hare Discussions

Hare opinions

Objective C

Objective C Retrospectives

Early predecessor: The Object Oriented Pre-Compiler by Brad Cox

related book: Object Oriented Programming: An Evolutionary Approach

Discussion about CTM language design

Beyond the PDP-11: Architectural support for a memory-safe C abstract machine is useful for designers of system languages making choices like how much pointer arithmetic to support. Reviews some parts of the C specification that allow C to be historically platform-agnostic (e.g. segmented memory models and microcontrollers with separate integer and address registers). Reviews some common C idioms that depend on behavior not mandated by the C standard (e.g. various kinds of pointer arithmetic). Considers some new CHERI instructions to better support C. Creates a list of implementation models that some various degrees of implementation-dependent behavior (such as pointer arithmetic), and describes which of the previously-listed idioms are supported by each model. Ported some test programs to one of the implementation models, and reported how much code had to be changed in the porting, and the effect on performance.


Footnotes:

1.