Add getchar()

parent 311fec8e
CC = gcc
CFLAGS = -fopenmp -Ideps -Os -pedantic -std=c99 -Wall -fstrict-aliasing -fomit-frame-pointer -D_GNU_SOURCE -DMS_WINDOWS
LDFLAGS = -static -static-libgcc -lm -lwsock32
CFLAGS = -fopenmp -Ideps -Os -pedantic -std=c99 -Wall -fstrict-aliasing -fomit-frame-pointer -D_GNU_SOURCE
LDFLAGS = -static -static-libgcc -lm
ifeq ($(OS),Windows_NT)
BIN = dukboot.exe
MAKEHEADERS = makeheaders.exe
CFLAGS += -DMS_WINDOWS
LDFLAGS += -lwsock32
else
BIN = dukboot
MAKEHEADERS = makeheaders
CFLAGS += -DUNIX
endif
SRC = $(wildcard src/*.c)
......
......@@ -119,7 +119,7 @@ Therefore, in these hosted environments it has the traditional garbage that come
with being a Linux / Windows / MacOS process. I provide access to these features,
not because I endorse them, but because they are unavoidable on these platforms.
### argv
### argv : [ String ]
Command line arguments! It's an array of strings, because while you might be under the
impression that the command line arguments should be one big string, shells
actually do split the argument string into tokens and pass each individual token.
......@@ -129,7 +129,7 @@ argument is actually argv[2] - but that's just how Operating Systems work, sorry
The first thing you should do with argv is pass it to a parser library like [`minimist`](https://github.com/substack/minimist)
to make it less ridiculous to work with.
### environ
### environ : { String : String }
Environment Variables! Those magical invisible global variables that hang around your
process like a ghost, subtly influencing branching and making it harder to figure
out why it works on my machine but not on yours. They're available in the `environ`
......@@ -141,6 +141,45 @@ are strings.
### print
This is just here temporarily until I add "proper" support for `stdout` or a proper `console.log` implementation.
### getchar : () => Number | Error EOF | undefined
This is a sweet and simple little function, and for now it's the only way to get at
the standard input stream (*stdin* in POSIX parlance). Working with binary input
is easier because it returns raw numbers rather than encoding them as text. Since
I mostly forsee *stdin* being used as an interface between programs I think
encouraging a binary protocol (like HTTP2 or Protobuf) makes sense here. String
parsing is so error-prone! For interactive input, eventually there will be a
console API, don't worry.
On Linux, `getchar` is non-blocking. If there is nothing available it will
return `undefined`. I haven't figured out how to make it non-blocking on Windows yet.
When you have consumed all the stdin, it will return (not throw!) an `Error: EOF` object.
Actual, legit I/O errors will get thrown, where the error code and message come
from the underlying operating system error messages.
... Wait... you're still reading? You must want text, not binary. Oh fine:
```
if (o.OpenMP.id === 0) {
var c
var chars = []
var text = ''
while (true) {
c = o.modules.getchar()
if (c !== undefined) chars.push(c)
if (c instanceof Error) break
}
text = String.fromCharCode.apply(null, chars)
}
```
### stdin : pull-stream Source
My plan is to take the `getchar()` function and wrap it into a [`pull-stream`](https://github.com/pull-stream/pull-stream),
then you can use all the lovely [pull-stream libraries](https://pull-stream.github.io/) to
process incoming data, and send it to...
### stdout : pull-stream Sink
## Parallel Computing
By default, Dukboot will start as many physical threads as you have logical cores.
......@@ -153,11 +192,11 @@ L1 cache misses and power consumption. This multiprocessing is achieved using th
OpenMP standard which is natively supported by most C compilers, rather than using
a non-portable threading library.
### OpenMP.numThreads
### OpenMP.numThreads : Number
This is the number of physical threads that are active. Right now, that is fixed
and equal to the number of logical cores.
### OpenMP.id
### OpenMP.id : Number
Each thread is assigned an `id`, which runs from 0 to OpenMP.numThreads - 1.
If for some reason you don't want to run multiple instances (e.g.
......@@ -200,7 +239,7 @@ terrible one; I don't know yet.
## Module System
### compile ( filepath : String ) : Function
### compile : (filepath : String) => Function
CommonJS (as the Node.js `require` style of module loading is called) is overly
complicated. Large portions of its behavior is unspecified or implicit. It is
common for beginners to be confused as to why `require('foobar.js')` works but
......@@ -229,7 +268,7 @@ Function files
## Clocks / Timing
### sleep ( milliseconds : Number ) : void
### sleep : (milliseconds : Number) => void
Suspends the current thread for the given number of milliseconds. Fractional
milliseconds are supported up to nanosecond resolution, but accuracy depends
on the underlying C library implementation of `nanosleep`.
......
function (o) {
// For fun, let every thread race to read from stdin. Quite amusing.
// Results will (probably) be printed out of order.
var c;
while (true) {
c = o.modules.getchar()
if (c == -1) break
o.modules.print(o.OpenMP.id + ': ' + c)
}
}
function (o) {
// If you want to read bytes in order, I would recommend having
// a single master thread read the bytes. You can divide the work later.
if (o.OpenMP.id === 0) {
var c;
while (true) {
c = o.modules.getchar()
o.modules.print(o.OpenMP.id + ': ' + c)
if (c instanceof Error) break
o.modules.sleep(100)
}
}
}
function (o) {
// If you want to read bytes in order, I would recommend having
// a single master thread read the bytes. You can divide the work later.
if (o.OpenMP.id === 0) {
var c;
var chars = [];
while (true) {
c = o.modules.getchar()
if (c !== undefined) chars.push(c)
if (c instanceof Error) break
}
o.modules.print(String.fromCharCode.apply(null, chars))
}
}
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include "duktape/duktape.h"
#include "c_getchar.h"
int c_getchar (duk_context *ctx) {
int c;
c = fgetc(stdin);
if (c != EOF) {
duk_push_int(ctx, c);
return 1;
}
if (feof(stdin)) {
duk_push_error_object(ctx, errno, "EOF");
return 1;
}
if (ferror(stdin)) {
if (errno == EWOULDBLOCK) {
duk_push_undefined(ctx);
return 1;
}
duk_push_error_object(ctx, errno, strerror(errno));
clearerr(stdin);
return duk_throw(ctx);
}
return duk_error(ctx, 999, "Unhandled situation in c_getchar.");
}
......@@ -4,6 +4,8 @@ extern char **environ;
// Include dependencies
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#ifdef _WIN32
// @see [1]
#include <io.h>
......@@ -23,10 +25,19 @@ int main(int argc, char *argv[] /* char *environ[] */) {
_setmode(1, _O_BINARY);
#endif
// In my experience, output buffering by default is surprising to most
// beginners. It's premature optimization. If users need buffering they
// beginners. It's premature optimization, and can easily lead to data loss
// if e.g. writing stdout to a log. If users need buffering they
// are smart enough to implement that in their app.
setbuf(stdin, NULL);
setbuf(stdout, NULL);
// Finally, async I/O is all the rage for performance these days. Since I fail
// to see why there should be two ways to do something if one way is better,
// we'll just try to have a consistant async I/O interface.
// Note: at the moment this only works on POSIX. :(
#ifdef UNIX
int flags = fcntl(STDIN_FILENO, F_GETFL, 0);
fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);
#endif
if (argc < 2) {
printf("Usage: dukboot file-to-eval.js\n");
......@@ -84,6 +95,8 @@ int main(int argc, char *argv[] /* char *environ[] */) {
duk_put_prop_string(ctx, -2, "assert_safe_path");
duk_push_c_function(ctx, native_sleep, 1);
duk_put_prop_string(ctx, -2, "sleep");
duk_push_c_function(ctx, c_getchar, 0);
duk_put_prop_string(ctx, -2, "getchar");
app_push_netLib(ctx);
duk_put_prop_string(ctx, -2, "netLib");
duk_put_prop_string(ctx, -2, "modules");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment