From 2475516bee0ca64ffb1bff184e671db9b69bd617 Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Mon, 21 Apr 2025 14:18:23 -0700 Subject: Chapter 22: A Hello, World! MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: godalming123 <68993177+godalming123@users.noreply.github.com> Co-authored-by: Tamás Hadházy <85063808+Hels15@users.noreply.github.com> Co-authored-by: Hsiang-Ying Fu <66062782+fuhsnn@users.noreply.github.com> Co-authored-by: fuhsnn <66062782+fuhsnn@users.noreply.github.com> Co-authored-by: Symious <14933944+symious@users.noreply.github.com> Co-authored-by: Jun Kawasaki Co-authored-by: Claude Opus 5 --- docs/chapter22/README.md | 220 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 220 insertions(+) create mode 100644 docs/chapter22/README.md (limited to 'docs/chapter22/README.md') diff --git a/docs/chapter22/README.md b/docs/chapter22/README.md new file mode 100644 index 00000000..94752cee --- /dev/null +++ b/docs/chapter22/README.md @@ -0,0 +1,220 @@ +# Chapter 22: A Hello, World! + +English | [日本語](README.ja.md) + +[Previous: Chapter 21](../chapter21/README.md) | +[Next: Chapter 23](../chapter23/README.md) + +It's high time for strings and printing! In this chapter we look at what it +takes to make a basic string - not really a full fledged String class, that +will wait for another chapter. + +```java +sys.io.p("Hello, World!"); +``` + +Yeah! A 1-liner `Hello, World`! This example can be found in +`examples/A_helloWorld.smp`. + + +Let's break it down. + +- `sys`: is Just A Variable Name. Like other variables, it is looked up in the + current scope... which looks completely empty. Every Simple program now starts + with the `sys` variable in-scope. `sys` itself is a normal `struct` + and is defined in `src/main/smp/sys.smp`. +- `sys.io.p`: lookup the `io` field in `sys` struct, which yields the `sys.io` + struct; then a lookup of `p` (short for "print") in the `sys.io` struct. + This returns a function. +- `p("Hello, World!")`: call the function just loaded from the `p` field, with + `"Hello, World!"` as a string argument. +- `"Hello, World!"`: New syntax that yields a constant array of `u8` (chars). + The array is otherwise a normal range-checked array, with a leading `#` + length field. The array backing data eventually ends up in the *constant + pool*, and from there in an ELF file's RODATA section. + + +Chapter 11's lazy memory partitioning continues here: the parser carries one +`$mem`, and BulkMemPhi/MemPhi discover field aliases during optimization. +Allocations consume a partial MemMerge and produce `{ptr, $mem}`; calls carry +whole memory. The loop Load search follows every arm of a memory merge, so +BrainFuck's invariant program length folds out of its main loop. + +You can also read [this chapter](https://github.com/SeaOfNodes/Simple/tree/linear-chapter22) in a linear Git revision history on the [linear](https://github.com/SeaOfNodes/Simple/tree/linear) branch and [compare](https://github.com/SeaOfNodes/Simple/compare/linear-chapter21...linear-chapter22) it to the previous chapter. + + +Here is the [complete language grammar](22-grammar.md) for this chapter. + +## Nested Types and Static Fields + +Simple now supports nested types - this is a name-space only change, so no new +semantics - just the ability to nest type definitions. + +```java +struct Outer { + struct Inner { + int in; + flt pi = 3.14; + }; + int out; +}; +``` + +Any final field set in the declaration (as opposed to the constructor) is +automatically a "static" field - it is final for all instances, and does not +need to be stored in each instance. Instead one copy is kept in a global +space (not really a *class* object yet) and loaded from there. + +In this example: + +- Instances of the `Outer` struct have a single `int` field `out`. +- Instances of the `Inner` struct have a single `int` field `in`. +- There exists an `Outer.Inner.pi` field in a global space with value `3.14`. + +The `sys` struct is another example, with nested `libc` and `io` structs at +least. All the fields in these structs are all final, hence static. + + + +## FFI and I/O Calls + +We can now call external / FFI functions, and they are declared like any other +variable except being assigned "C". Here is the binding for the libc `write` +call: + +`{ i32 fd, i64 buf, u32 len -> u32 } write = "C";` + +Any final variable assigned as "C" will be considered defined externally. The +syntax of this might change slightly to avoid ambiguity with assigning the same +"C" string. + +There is no linker support for getting the correct signature or namespace; the +name `write` has to be unique when linking. + + +## Auto-Import and the "sys." namespace + +The `sys` struct includes `libc` bindings, and any default library code +including e.g. printing support and collections. As of this chapter it +includes minimal libc bindings and an easy print. This excerpt shows the +parts used above: + +```java +// top-level default import +struct sys { + // libc bindings + /** https://www.man7.org/linux/man-pages/man2/.2.html */ + struct libc { + // fd buf len -> len + { i32 i64 u32 -> u32 } write = "C"; + // addr len prot flags fd off -> void* + { i64 i64 i32 i32 i32 i32 -> i64 } mmap = "C"; + // mmap flags + i32 PROT_EXEC = 4; + i32 PROT_READ = 1; + i32 PROT_WRITE= 2; + i32 PROT_NONE = 0; + i32 MAP_PRIVATE = 2; + i32 MAP_ANON = 32; + }; + struct io { + val p = { u8[~] str -> + i64 ptr = str; // cast array base to i64 + return sys.libc.write(1,ptr,str#); + }; + }; +}; +``` + +The `sys` import itself is Just Another `struct` like any other struct; it has +fields and assignments and types declared internally. Note that these +assignments are all final and in the struct declaration, hence these are all +*static* fields. + + +## Casting a Pointer to an `i64` + +You can now cast a pointer to an i64, although not the other way around. +`i64 ptr = str; // cast array base to i64` + +For arrays, this cast is to the array *base* skipping the `#` length field, and +is suitable for passing to external "C" calls. For structs, this is just the +struct base. This is used to pass a length-checked Simple array to the libc +`write` call in the above `sys.io.p` function. + + +## Arrays of Constants and Constant Arrays + +The Simple type system now supports the notion of a "constant array" - an array +of fixed constants, stored in the ELF file in the RODATA section. Currently we +only support strings at the parser level: `"Hello, World!"` makes a `u8[13]` +array containing 13 ASCII bytes; like all arrays it has a length and will be +range-checked at some point. + +A constant version of a non-constant array can be assigned using the `u8[~]` +syntax. + +```java +// Make a mutable array of ints +int N=4; +i32[] is = new i32[N]; +// Mutate the array +for( int i=0; i // Notice the [~] signature marking nums as immutable + int sum = 0; + for( int i=0; i +| Test cohort | Cases D / X | Ch 20: Ops / RA / X | Ch 21: Ops / RA / X | Ch 22: Ops / RA / X | +|---|---:|---:|---:|---:| +| Ch 20 | 22 / 13 | — / — / 161 | 55,173 / 5,407 / 174 | 58,751 / 9,099 / 175 | +| Ch 21 | 20 / 18 | | 6,207 / 261 / 461 | 6,324 / 379 / 475 | +| Ch 22 | 14 / 8 | | | 198 / 60 / 7 | + -- cgit v1.2.3