summaryrefslogtreecommitdiff
path: root/src/main/smp
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2026-08-10 11:11:18 -0700
committerCliff Click <cliffc@acm.org>2026-08-10 15:21:17 -0700
commit28209703c72fb7e9955961dbdb84e24a0f871acd (patch)
tree8e4df2c1a5e6811696b454bbc4d7cd1cc30e42e7 /src/main/smp
parente57bdd315a7918a0d130e1c95648ca4a1b4f08ed (diff)
downloadSimple-linear-chapter25.tar.gz
Simple-linear-chapter25.zip
Chapter 25: Moduleslinear-chapter25
Diffstat (limited to 'src/main/smp')
-rw-r--r--src/main/smp/sys.smp202
-rw-r--r--src/main/smp/sys/Ary.smp10
-rw-r--r--src/main/smp/sys/Scan.smp36
-rw-r--r--src/main/smp/sys/aryi64.smp70
-rw-r--r--src/main/smp/sys/aryu8.smp66
-rw-r--r--src/main/smp/sys/io.smp32
-rw-r--r--src/main/smp/sys/libc.smp20
7 files changed, 240 insertions, 196 deletions
diff --git a/src/main/smp/sys.smp b/src/main/smp/sys.smp
index cda93abe..815212ab 100644
--- a/src/main/smp/sys.smp
+++ b/src/main/smp/sys.smp
@@ -1,199 +1,9 @@
// -*- mode: java; -*-
-// top-level default import
-struct sys {
- // libc bindings
- /** https://www.man7.org/linux/man-pages/man2/<libc call>.2.html */
- struct libc {
- i32 errno = "C";
- // fd buf len -> len
- { i32 i64 u32 -> u32 } write = "C";
- // fd buf len -> len
- { i32 i64 u32 -> u32 } read = "C";
- // status
- { i32 -> i32 } _exit = "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 ary {
- val copy = { u8[] dst, int doff, u8[~] src, int soff, int len ->
- for( int i=0; i<len; i++ )
- dst[i+doff] = src[i+soff];
- return dst;
- };
- };
+// top-level default standard library import
- // Ary/Vector of u8s - a resizable array of bytes.
- struct aryu8 {
- u32 !len = 0;
- u8[] !buf;
- // Grow as needed
- val _grow = { int len2 ->
- if( len+len2 < buf# ) return self;
- int newlen = buf# * 2;
- while( len+len2 >= newlen ) newlen *= 2;
- u8[] buf2 = new u8[newlen];
- sys.ary.copy(buf2,0,buf,0,buf#);
- buf = buf2;
- self;
- };
-
- // Add a character to aryu8
- val add = { u8 c ->
- _grow(1).buf[len++] = c;
- self;
- };
-
- // Add a plain array to the aryu8
- val addAry = { u8[~] ary ->
- _grow(ary#);
- sys.ary.copy(buf,len,ary,0,ary#);
- len += ary#;
- self;
- };
-
- // Remove x chars from the tail.
- val undo = { int x ->
- len = len >= x ? len-x : 0;
- self;
- };
-
- // Print ASCII number into aryu8
- val num = { i64 x ->
- if( !x ) { add('0'); return self; }
- if( x < 0 ) { add('-'); x = -x; }
- i64 ten = 1, log = 0;
- while( ten <= x ) { ten *= 10; log++; }
- for( int i=0; i<log; i++ ) {
- ten /= 10;
- i64 q = x/ten;
- u8 digit = q+'0';
- add(digit);
- x -= q*ten;
- }
- self;
- };
-
- // Write to fd, returns the number of bytes written (len).
- val write = { i32 fd -> i64 ptr = buf; sys.libc.write(fd,ptr,len); };
- };
-
-
- // Scanner; a simple string parser.
- struct Scan {
- int !x;
- u8[~] buf;
-
- // Skip whitespace
- val skip = { ->
- while( buf[x] <= ' ' )
- x++;
- return self;
- };
-
- // True if find required character, and skip it; else False.
- val match = { u8 c ->
- skip();
- if( buf[x] != c ) return false;
- x++;
- return true;
- };
-
- // Parse a simple number
- val number = { ->
- skip();
- bool neg = match('-');
- i64 n = 0;
- while( '0' <= buf[x] & buf[x] <= '9' )
- n = n*10 + (buf[x++] - '0');
- return neg ? -n : n;
- };
- };
-
- // Ary/Vector of i64s
- struct aryi64 {
- u32 !len = 0;
- i64[] !buf;
- // Grow as needed
- val _grow = { int len2 ->
- if( len+len2 < buf# ) return self;
- int newlen = buf# * 2;
- while( len+len2 >= newlen ) newlen *= 2;
- i64[] buf2 = new i64[newlen];
- for( int i=0; i<buf#; i++ )
- buf2[i] = buf[i];
- buf = buf2;
- self;
- };
-
- // Add a i64 to aryu8
- val add = { i64 c ->
- _grow(1).buf[len++] = c;
- self;
- };
-
- // Write to aryu8
- val str = { ->
- // TODO: clunky default constructor syntax
- var str = new sys.aryu8 { buf = new u8[1]; }.add('[');
- for( int i=0; i<len; i++ )
- str.num(buf[i]).add(',').add(' ');
- str.undo(2).add(']');
- };
- };
-
- // Parse a comma-separated list of numbers, wrapped with square brackets,
- // with optional whitespace e.g. "[ -17, 0,99,12456789 ] ".
- // parseAryI64 & aryi64.str are bijections modulo whitespace.
- // Returns null if the parse failed.
- val parseAryI64 = { u8[~] str ->
- sys.Scan !s = new sys.Scan{ buf = str; };
- if( !s.match('[') ) return null;
- // Array of parsed results
- var nums = new sys.aryi64{ buf=new i64[1]; };
- // Parse a list of numbers
- if( !s.match(']') ) { // Handle the empty list
- // Parse "123, " until a something else
- while( s.x < s.buf# ) {
- nums.add(s.number());
- if( !s.match(',') )
- break;
- };
- if( !s.match(']') ) return null; // Close the list
- }
- return nums;
- };
-
- struct io {
- // Print string to stdout
- val p = { u8[~] str ->
- i64 ptr = str; // cast array base to i64
- return sys.libc.write(1,ptr,str#);
- };
-
- // Return stdin as a u8[]
- val stdin = { ->
- var buf = new u8[8*1024];
- i64 ptr = buf;
- int len = 0;
- while( true ) {
- int rez = sys.libc.read(0,ptr,buf#);
- if( rez < 0 ) sys.libc._exit(sys.libc.errno);
- len += rez;
- if( rez < buf# )
- break;
- sys.libc._exit(-2); // Need to implement
- }
- // Compress
- var dst = new u8[len];
- return sys.ary.copy(dst,0,buf,0,len);
- };
- };
-};
+var libc = libc;
+var io = io;
+var aryu8 = aryu8;
+var Scan = Scan;
+var aryi64 = aryi64;
diff --git a/src/main/smp/sys/Ary.smp b/src/main/smp/sys/Ary.smp
new file mode 100644
index 00000000..0f667695
--- /dev/null
+++ b/src/main/smp/sys/Ary.smp
@@ -0,0 +1,10 @@
+// -*- mode: java; -*-
+
+// Array utilities
+
+// Copy byte array contents
+val copy = { u8[] dst, int doff, u8[~] src, int soff, int len ->
+ for( int i=0; i<len; i++ )
+ dst[i+doff] = src[i+soff];
+ return dst;
+};
diff --git a/src/main/smp/sys/Scan.smp b/src/main/smp/sys/Scan.smp
new file mode 100644
index 00000000..acf366f7
--- /dev/null
+++ b/src/main/smp/sys/Scan.smp
@@ -0,0 +1,36 @@
+// -*- mode: java; -*-
+
+// Scanner; a simple string parser.
+struct Scan {
+ int !x; // Cursor
+ u8[~] buf; // Immutable byte array, must be set in 2nd constructor
+
+ new Scan = { u8[~] b ->
+ buf = b;
+ };
+
+ // Skip whitespace
+ val skip = { ->
+ while( x < buf# && buf[x] <= ' ' )
+ x++;
+ return self;
+ };
+
+ // True if find required character, and skip it; else False.
+ val match = { u8 c ->
+ skip();
+ if( buf[x] != c ) return false;
+ x++;
+ return true;
+ };
+
+ // Parse a simple number
+ val number = { ->
+ skip();
+ bool neg = match('-');
+ i64 n = 0;
+ while( '0' <= buf[x] & buf[x] <= '9' )
+ n = n*10 + (buf[x++] - '0');
+ return neg ? -n : n;
+ };
+};
diff --git a/src/main/smp/sys/aryi64.smp b/src/main/smp/sys/aryi64.smp
new file mode 100644
index 00000000..8a154f34
--- /dev/null
+++ b/src/main/smp/sys/aryi64.smp
@@ -0,0 +1,70 @@
+// -*- mode: java; -*-
+
+// Ary/Vector of i64s
+struct aryi64 {
+ u32 !len = 0;
+ i64[] !buf;
+
+ new aryi64 = { i64[] b ->
+ buf = b;
+ };
+
+ // Grow as needed
+ val _grow = { int len2 ->
+ if( len+len2 < buf# ) return self;
+ int newlen = buf# * 2;
+ while( len+len2 >= newlen ) newlen *= 2;
+ i64[] !buf2 = new i64[newlen];
+ for( int i=0; i<buf#; i++ )
+ buf2[i] = buf[i];
+ buf = buf2;
+ self;
+ };
+
+ // Add a i64 to aryu8
+ val add = { i64 c ->
+ _grow(1).buf[len++] = c;
+ self;
+ };
+
+ // Write to aryu8
+ val str = { ->
+ // TODO: clunky default constructor syntax
+ var str = new aryu8(new u8[1]).add('[');
+ for( int i=0; i<len; i++ )
+ str.num(buf[i]).add(',').add(' ');
+ str.undo(2).add(']');
+ };
+
+ val swap = { int i, int j ->
+ i64 tmp = buf[i];
+ buf[i] = buf[j];
+ buf[j] = tmp;
+ self;
+ };
+};
+
+
+// Parse a comma-separated list of numbers, wrapped with square brackets,
+// with optional whitespace e.g. "[ -17, 0,99,12456789 ] ".
+// parseAryI64 & aryi64.str are bijections modulo whitespace.
+// Returns null if the parse failed, without giving a clue as to why.
+val parseAryI64 = { u8[~] str ->
+ Scan !s = new Scan(str);
+ if( !s.match('[') ) return null;
+ // Array of parsed results
+ var nums = new aryi64(new i64[1]);
+
+ // Parse a list of numbers
+ if( s.match(']') ) // Handle the empty list
+ return nums;
+
+ // Parse number commas "123, " until something else
+ while( s.x < s.buf# ) {
+ nums.add(s.number());
+ if( !s.match(',') )
+ break;
+ };
+ // Close the list
+ return s.match(']') ? nums : null;
+};
diff --git a/src/main/smp/sys/aryu8.smp b/src/main/smp/sys/aryu8.smp
new file mode 100644
index 00000000..0ce4bd23
--- /dev/null
+++ b/src/main/smp/sys/aryu8.smp
@@ -0,0 +1,66 @@
+// -*- mode: java; -*-
+
+// Ary/Vector of u8s - a resizable array of bytes.
+struct aryu8 {
+ u32 !len = 0;
+ u8[] !buf;
+
+ new aryu8 = { u8[] b ->
+ buf = b;
+ };
+
+ // Grow as needed
+ val _grow = { int len2 ->
+ if( len+len2 < buf# ) return self;
+ int newlen = buf# * 2;
+ while( len+len2 >= newlen ) newlen *= 2;
+ u8[] !buf2 = new u8[newlen];
+ Ary.copy(buf2,0,buf,0,buf#);
+ buf = buf2;
+ self;
+ };
+
+ val clear = { -> len = 0; self; };
+
+ // Add a character to aryu8
+ val add = { u8 c ->
+ _grow(1).buf[len++] = c;
+ self;
+ };
+
+ // Add all of a plain array to the aryu8
+ val addAry = { u8[~] ary ->
+ _grow(ary#);
+ Ary.copy(buf,len,ary,0,ary#);
+ len += ary#;
+ self;
+ };
+
+ // Remove x bytes from the tail.
+ val undo = { int x ->
+ len = len >= x ? len-x : 0;
+ self;
+ };
+
+ // Print ASCII number into aryu8
+ val num = { i64 x ->
+ if( !x ) { add('0'); return self; }
+ if( x < 0 ) { add('-'); x = -x; }
+ i64 ten = 1, log = 0;
+ while( ten <= x ) { ten *= 10; log++; }
+ for( int i=0; i<log; i++ ) {
+ ten /= 10;
+ i64 q = x/ten;
+ u8 digit = q+'0';
+ add(digit);
+ x -= q*ten;
+ }
+ self;
+ };
+
+ // Write to fd, returns the number of bytes written (len).
+ val write = { i32 fd ->
+ i64 ptr = buf; // Cast buffer pointer to i64 for native C call
+ libc.write(fd,ptr,len);
+ };
+};
diff --git a/src/main/smp/sys/io.smp b/src/main/smp/sys/io.smp
new file mode 100644
index 00000000..b94e89fa
--- /dev/null
+++ b/src/main/smp/sys/io.smp
@@ -0,0 +1,32 @@
+// -*- mode: java; -*-
+
+// Simple I/O Library
+
+// Write byte array to stdout
+val p = { u8[~] str ->
+ i64 ptr = str; // cast array base to i64
+ return libc.write(1,ptr,str#);
+};
+
+val p_noInline = { u8[~] str ->
+ i64 ptr = str; // cast array base to i64
+ return libc.write(1,ptr,str#);
+};
+
+// Return stdin as a u8[].
+val stdin = { ->
+ var buf = new u8[8*1024];
+ i64 ptr = buf;
+ int len = 0;
+ while( true ) {
+ int rez = libc.read(0,ptr,buf#);
+ if( rez < 0 ) libc._exit(libc.errno);
+ len += rez;
+ if( rez < buf# )
+ break;
+ libc._exit(-2); // Need to implement for more than 8K input
+ }
+ // Compress
+ var dst = new u8[len];
+ return Ary.copy(dst,0,buf,0,len);
+};
diff --git a/src/main/smp/sys/libc.smp b/src/main/smp/sys/libc.smp
new file mode 100644
index 00000000..42fc7313
--- /dev/null
+++ b/src/main/smp/sys/libc.smp
@@ -0,0 +1,20 @@
+// -*- mode: java; -*-
+
+// libc bindings:
+// https://www.man7.org/linux/man-pages/man2/<libc call>.2.html
+
+// fd buf len -> len
+{ i32 i64 u32 -> u32 } write = "C";
+// fd buf len -> len
+{ i32 i64 u32 -> u32 } read = "C";
+// status
+{ i32 -> i32 } _exit = "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;