utility functions for implementing noja functions in c; built-ins for socket management; http server draft in noja

This commit is contained in:
Francesco Cozzuto
2023-01-15 00:51:36 +01:00
parent 2c7e0ae2ba
commit 74a1648af8
27 changed files with 1705 additions and 299 deletions
+101
View File
@@ -0,0 +1,101 @@
cat = string.cat;
fun Node(T) {
K = { item: T, next: none };
K.next = ?K;
return K;
}
fun makeNode(item)
return { item: item, next: none };
fun Stack(T)
return { size: int, head: ?Node(T) };
fun StackIterator(T)
return { stack: Stack(T), index: ?int, cursor: ?Node(T), next: Callable };
fun makeStackIterator(stack: Stack(any))
return {
stack: stack,
index: none,
cursor: stack.head,
fun toString(iter: StackIterator(stack.type))
return cat("StackIterator(", toString(stack), ")");
fun next(iter: StackIterator(stack.type)) {
if iter.index == none: {
iter.index = 0;
iter.cursor = iter.stack.head;
} else {
if iter.cursor != none: {
iter.cursor = iter.cursor.next;
iter.index = iter.index+1;
}
}
if iter.cursor == none:
return none;
return iter.cursor.item, iter.index;
}
fun haveNext(iter: StackIterator(stack.type)) {
return none != iter.cursor
and none != iter.cursor.next;
}
};
fun makeStack(T)
return {
type: T,
size: 0,
head: none,
fun push(stack: Stack(T), item: T) {
if item == none:
error("Can't insert \"none\" into a stack");
node = makeNode(item);
node.next = stack.head;
stack.head = node;
stack.size = stack.size+1;
}
fun pop(stack: Stack(T)) {
node = stack.head;
if node != none: {
stack.head = node.next;
stack.size = stack.size-1;
}
return node.item;
}
fun isEmpty(stack: Stack(T))
return stack.head == none;
fun toString(stack: Stack(T)) {
iter = stack->iter();
s = "[";
while (item = iter->next()) != none: {
s = cat(s, toString(item));
if iter->haveNext():
s = cat(s, ", ");
}
s = cat(s, "]");
return s;
}
fun print(stack: Stack(T))
print(stack->toString());
fun iter(stack: Stack(T))
return makeStackIterator(stack);
};
return {
Stack: Stack,
makeStack: makeStack,
StackIterator: StackIterator,
makeStackIterator: makeStackIterator
};
+17
View File
@@ -0,0 +1,17 @@
stack, error = import("stack.noja");
print(error, "\n");
Stack = stack.Stack;
makeStack = stack.makeStack;
StackIterator = stack.StackIterator;
makeStackIterator = stack.makeStackIterator;
s1 = makeStack(int);
s1->push(1);
s1->push(2);
s1->push(3);
print(toString(s1), "\n");
iter = makeIterator(s1);
print(toString(iter), "\n");
while (val, key = iter->next()) != none:
print(key, " => ", val, "\n");
+12 -3
View File
@@ -260,7 +260,7 @@ fun parseVersion(scan: Scanner) {
char = scan->consume(5); char = scan->consume(5);
major = ord(char) - ord("0"); major = ord(char) - ord("0");
if scan->hint(1) != "." and isDigit(scan->hint(2)): { if scan->hint(1) == "." and isDigit(scan->hint(2)): {
char = scan->consume(2); char = scan->consume(2);
minor = ord(char) - ord("0"); minor = ord(char) - ord("0");
} else } else
@@ -316,9 +316,10 @@ fun parse(src: String) {
if error != none: if error != none:
return none, error; return none, error;
if scan->hint(0) != "\r" or scan->hint(1) != "\n": if scan->hint(0) != "\r" or scan->hint(1) != "\n": {
# Request with no headers or body # Request with no headers or body
return newRequest(method, url, version); return newRequest(method, url, version);
}
scan->consume(2); # Consume the "\r\n" scan->consume(2); # Consume the "\r\n"
headers = {}; headers = {};
@@ -344,10 +345,18 @@ fun parse(src: String) {
} while char != "\r" and char != none; } while char != "\r" and char != none;
headers[name] = body; headers[name] = body;
if char == "\r": {
if scan->hint(1) != "\n":
return none, "Missing \\n after \\r in header body";
scan->consume(2);
}
} }
if char != none: if char != none: {
assert(char == "\r" and scan->hint(1) == "\n");
scan->consume(2); # Consume the final "\r\n" scan->consume(2); # Consume the final "\r\n"
}
return newRequest(method, url, version, headers); return newRequest(method, url, version, headers);
} }
+109
View File
@@ -0,0 +1,109 @@
server = import("server.noja");
request = import("request.noja");
response = import("response.noja");
Request = request.Request;
fun loadFile(path: String) {
stream, error = files.openFile(path, files.READ);
if error != none:
return none, error;
text = "";
size = 512;
do {
size = 2 * size;
data = buffer.new(size);
num_bytes, error = files.read(stream, data);
if error != none:
return none, error;
text = string.cat(text, buffer.toString(data));
} while num_bytes == size;
files.close(stream);
return text;
}
fun generateHome() {
title = "Homepage";
content = "Hello, world!";
return string.cat(
"<html>",
"<head>",
"<title>", title, "</title>",
"</head>",
"<body>",
"<a>", content, "</a>",
"</body>",
"</html>"
);
}
fun generateLogout() {
}
table = {
'/': 'login.html',
'/home': generateHome,
'/login': 'login.html',
'/signup': 'signup.html',
'/logout': generateLogout
};
default = 'notfound.html';
debug = true;
fun callback(req: Request) {
path = req.url.path;
if path == "/" or path == "/login": {
if req.method == "POST": {
} else if req.method == "GET"; {
} else {
assert(false);
}
}
# Generate response body
{
entry = table[req.url.path];
if entry == none: {
entry = default;
not_found = true;
} else
not_found = false;
if isCallable(entry):
data, error = entry(req);
else
data, error = loadFile(entry);
}
# Generate response object
{
if error == none:
res = response.new(200, data);
else if not_found:
res = response.new(404, data);
else if debug:
res = response.new(500, error);
else
res = response.new(500);
}
return res;
}
s, err = server.new(callback, "127.0.0.1", 8080);
if s == none: error(err);
s->loop(); # This doesn't return
s->close();
+12
View File
@@ -0,0 +1,12 @@
<html>
<head>
<title>Login page</title>
</head>
<body>
<form action="/login" method="POST">
<input type="text" name="user" placeholder="[Username]" />
<input type="password" name="pass" placeholder="[Password]" />
<input type="submit" value="Enter" />
</form>
</body>
</html>
+359
View File
@@ -0,0 +1,359 @@
scanner = import("scanner.noja");
newScanner = scanner.newScanner;
Scanner = scanner.Scanner;
isDigit = scanner.isDigit;
ord = string.ord;
cat = string.cat;
fun isAlpha(char: String)
return (ord(char) >= ord('a') and ord(char) <= ord('z'))
or (ord(char) >= ord('A') and ord(char) <= ord('Z'));
fun isUpper(char: String)
return ord(char) >= ord('A')
and ord(char) <= ord('Z');
fun isUnreserved(char: String)
return isAlpha(char)
or isDigit(char)
or char == "-"
or char == "."
or char == "_"
or char == "~";
fun isSubdelim(char: String)
return char == "!" or char == "$"
or char == "&" or char == "'"
or char == "(" or char == ")"
or char == "*" or char == "+"
or char == "," or char == ";"
or char == "=";
fun isPChar(char: String)
return isUnreserved(char)
or isSubdelim(char)
or char == ":"
or char == "@";
fun parseMethod(scan: Scanner) {
char = scan->current();
if char == none or not isUpper(char):
return none, "Missing method";
method = "";
do {
method = cat(method, char);
char = scan->consume();
} while char != none and isUpper(char);
return method, none;
}
fun parsePort(scan: Scanner) {
port = none;
char = scan->current();
if char == ":" and isDigit(scan->hint(1)): {
char = scan->consume(); # Skip the ":"
port = 0;
do {
digit = ord(char) - ord("0");
port = port * 10 + digit;
char = scan->consume();
} while char != none and isDigit(char);
}
return port;
}
fun isHostname(char: String)
return isUnreserved(char)
or isSubdelim(char);
fun parseHost(scan: Scanner) {
char = scan->current();
if char == none:
return none, "Missing host";
if not isHostname(char):
return none, "Invalid character in place of host";
name = "";
do {
name = cat(name, char);
char = scan->consume();
} while char != none and isHostname(char);
port = parsePort(scan);
return {name: name, port: port};
}
fun parsePath(scan: Scanner) {
path = "";
char = scan->current();
if char != none and char == "/": {
path = cat(path, char);
char = scan->consume();
} else {
if char == none or not isPChar(char):
return none, "Invalid character in place of path";
}
while char != none and isPChar(char): {
do {
path = cat(path, char);
char = scan->consume();
} while char != none and isPChar(char);
if char == none or char != "/":
break;
path = cat(path, char);
char = scan->consume();
}
return path;
}
fun isQuery(char: ?String) {
if char == none:
return false;
return isPChar(char)
or char == "/"
or char == "?";
}
fun parseQuery(scan: Scanner) {
char = scan->current();
if char != none and char == "?": {
query = "";
while isQuery(char = scan->consume()):
query = cat(query, char);
} else
query = none;
return query;
}
fun isFragment(char: ?String) {
if char == none:
return false;
return isPChar(char) or char == "/";
}
fun parseFragment(scan: Scanner) {
char = scan->current();
if char != none and char == "#": {
fragment = "";
while isFragment(char = scan->consume()):
fragment = cat(fragment, char);
} else
fragment = none;
return fragment;
}
fun isSchemaFirst(char: ?String) {
return char != none and isAlpha(char);
}
fun isSchema(char: ?String)
return char != none
and (isAlpha(char)
or isDigit(char)
or char == "+"
or char == "-"
or char == ".");
fun parseSchema(scan: Scanner) {
char = scan->current();
schema = none;
if isSchemaFirst(char): {
start = scan.i;
schema = "";
do {
schema = cat(schema, char);
char = scan->consume();
} while isSchema(char);
if char == ":":
scan->consume();
else {
schema = none;
scan.i = start;
}
}
return schema;
}
fun followsAuthority(scan: Scanner)
return scan->current() == "/" and scan->hint(1) == "/";
fun parseURL(scan: Scanner) {
schema = parseSchema(scan);
if followsAuthority(scan): {
scan->consume(2);
host, error = parseHost(scan);
if error != none:
return none, error;
path = none;
if scan->current() == "/":
path = parsePath(scan);
} else {
host = none;
char = scan->current();
if char == none or char == "?" or char == "#":
return none, "Missing path";
path, error = parsePath(scan);
if error != none:
return none, error;
}
query = parseQuery(scan);
fragment = parseFragment(scan);
return {
host : host,
path : path,
query : query,
schema : schema,
fragment: fragment
};
}
fun parseVersion(scan: Scanner) {
if scan->hint(0) != "H" or scan->hint(1) != "T"
or scan->hint(2) != "T" or scan->hint(3) != "P"
or scan->hint(4) != "/" or not isDigit(scan->hint(5)):
return none, "Invalid version token";
char = scan->consume(5);
major = ord(char) - ord("0");
if scan->hint(1) == "." and isDigit(scan->hint(2)): {
char = scan->consume(2);
minor = ord(char) - ord("0");
} else
minor = 0;
scan->consume();
return {major: major, minor: minor};
}
fun parseHead(src: String) {
scan = newScanner(src);
method, error = parseMethod(scan);
if error != none:
return none, error;
char = scan->current();
if char == none or char != " ":
return none, "Missing space after method";
scan->consume();
url, error = parseURL(scan);
if error != none:
return none, error;
char = scan->current();
if char != " ":
return none, "Missing space after URL";
scan->consume();
version, error = parseVersion(scan);
if error != none:
return none, error;
if scan->hint(0) != "\r"
or scan->hint(1) != "\n": {
# Request with no headers or body
return {
method: method,
url: url,
version:
version,
headers: {},
body: none
}, none, scan.i;
}
scan->consume(2); # Consume the "\r\n"
headers = {};
while (char = scan->hint(0)) != none and (char != "\r" or scan->hint(1) != "\n"): {
# Header name until the next ":"
name = "";
do {
name = cat(name, char);
char = scan->consume();
} while char != ":" and char != none;
if char == none:
return none, "Invalid header (missing \":\" after name)";
scan->consume();
char = scan->consumeSpaces();
# Header body until \r
body = "";
do {
body = cat(body, char);
char = scan->consume();
} while char != "\r" and char != none;
headers[name] = body;
if char == "\r": {
if scan->hint(1) != "\n":
return none, "Missing \\n after \\r in header body";
scan->consume(2);
}
}
if char != none: {
assert(char == "\r" and scan->hint(1) == "\n");
scan->consume(2); # Consume the final "\r\n"
}
return {
method: method,
url: url,
version: version,
headers: headers,
body: none
}, none, scan.i;
}
fun test() {
sample = cat(
"GET /search?client=firefox-b-d&q=http+request+dataset HTTP/2\r\n",
"Host: www.google.com\r\n",
"user-agent: curl/7.84.0\r\n",
"accept: */*\r\n",
"\r\n");
request, error = parseHead(sample);
print("request=", request, "\n");
print("error=", error, "\n");
}
return {parseHead: parseHead, test: test};
+87
View File
@@ -0,0 +1,87 @@
parseHead = import("parse.noja").parseHead;
Version = {
major: int,
minor: int
};
Host = {
name: String,
port: ?int
};
URL = {
schema: ?String,
host : ?Host,
path : String,
query : ?String,
fragment: ?String
};
Request = {
method : String,
url : URL,
version: Version,
body : None | Buffer | [Buffer, Buffer]
};
fun fromSocket(fd: int, max_head: int = 1024) {
{
# Create a buffer to hold the received bytes
head_buffer = buffer.new(max_head);
# Receive bytes up to the buffer's size
received_bytes, error = net.recv(fd, head_buffer);
if error != none:
return none, error, true;
# Downsize the buffer to the received amount
head_buffer = buffer.sliceUp(head_buffer, 0, received_bytes);
assert(count(head_buffer) == received_bytes);
# Convert the received data to a string
text, error = buffer.toString(head_buffer);
if error != none:
return none, error, false; # Request isn't UTF-8
}
{
# Parse the request head's information
request, error, parsed_bytes = parseHead(text);
if error != none:
return none, error, false;
# Split the received data into head and body.
head_bytes = buffer.sliceUp(head_buffer, 0, parsed_bytes);
body_bytes = buffer.sliceUp(head_buffer, parsed_bytes, count(head_buffer) - parsed_bytes);
}
# Receive the remaining portion of the
# request's body (if it wasn't already)
{
content_length = request.headers['Content-Length'];
if content_length == none:
content_length = 0;
# TODO: Limit the content length
waiting_bytes = content_length - count(body_bytes);
assert(waiting_bytes >= 0);
if waiting_bytes > 0: {
second_half = buffer.new(waiting_bytes);
received_bytes, error = net.recv(fd, second_half);
if error != none:
return none, error, true; # Failed to receive the second half of the body
assert(received_bytes == waiting_bytes);
body_bytes = [body_bytes, second_half];
}
}
request.body = body_bytes;
return request;
}
+113
View File
@@ -0,0 +1,113 @@
Response = {
status: int,
body: Buffer,
headers: Map
};
status_table = {
100: "Continue",
101: "Switching Protocols",
102: "Processing",
200: "OK",
201: "Created",
202: "Accepted",
203: "Non-Authoritative Information",
204: "No Content",
205: "Reset Content",
206: "Partial Content",
207: "Multi-Status",
208: "Already Reported",
300: "Multiple Choices",
301: "Moved Permanently",
302: "Found",
303: "See Other",
304: "Not Modified",
305: "Use Proxy",
306: "Switch Proxy",
307: "Temporary Redirect",
308: "Permanent Redirect",
400: "Bad Request",
401: "Unauthorized",
402: "Payment Required",
403: "Forbidden",
404: "Not Found",
405: "Method Not Allowed",
406: "Not Acceptable",
407: "Proxy Authentication Required",
408: "Request Timeout",
409: "Conflict",
410: "Gone",
411: "Length Required",
412: "Precondition Failed",
413: "Request Entity Too Large",
414: "Request-URI Too Long",
415: "Unsupported Media Type",
416: "Requested Range Not Satisfiable",
417: "Expectation Failed",
418: "I'm a teapot",
420: "Enhance your calm",
422: "Unprocessable Entity",
426: "Upgrade Required",
429: "Too many requests",
431: "Request Header Fields Too Large",
449: "Retry With",
451: "Unavailable For Legal Reasons",
500: "Internal Server Error",
501: "Not Implemented",
502: "Bad Gateway",
503: "Service Unavailable",
504: "Gateway Timeout",
505: "HTTP Version Not Supported",
509: "Bandwidth Limit Exceeded"
};
fun getStatusText(code: int) {
text = status_table[code];
if text == none:
return "???";
return text;
}
fun new(status: int = 200,
body: Buffer | String = "",
headers: Map = {}) {
if type(body) == String:
body = buffer.fromString(body);
# It's important to cast the body from a
# string to a buffer before calculating
# the Content-Length because the count of
# a string doesn't necessarily match the
# number of bytes!
headers['Content-Length'] = count(body);
return {status: status, body: body, headers: headers};
}
fun toBuffer(res: Response) {
cat = string.cat;
text = cat("HTTP/1.0 ", toString(res.status), " ", getStatusText(res.status), "\r\n");
i = 0;
header_names = keysof(res.headers);
while i < count(header_names): {
name = header_names[i];
body = toString(res.headers[name]);
text = cat(text, name, ": ", body, "\r\n");
i = i+1;
}
text = cat(text, "\r\n", buffer.toString(res.body));
return buffer.fromString(text);
}
fun toSocket(fd: int, res: Response) {
buf = toBuffer(res);
_, error = net.send(fd, buf);
return error;
}
+56
View File
@@ -0,0 +1,56 @@
Scanner = {
src: String,
i: int,
hint: Callable,
current: Callable,
consume: Callable,
consumeSpaces: Callable
};
fun newScanner(src: String) {
fun isSpace(c: String)
return c == ' '
or c == '\t'
or c == '\n';
scan = {
src: src,
i: 0,
fun hint(scan: Scanner, n: int = 1) {
k = scan.i + n;
if k < count(scan.src):
return scan.src[k];
return none;
}
fun current(scan: Scanner)
return scan->hint(0);
fun consume(scan: Scanner, n: int = 1) {
assert(n > 0);
limit = count(scan.src);
scan.i = min(scan.i + n, limit);
return scan->current();
}
fun consumeSpaces(scan: Scanner) {
char = scan->current();
while isSpace(char):
char = scan->consume();
return char;
}
};
assert(istypeof(Scanner, scan));
return scan;
}
fun isDigit(char: ?String) {
if char == none:
return false;
ord = string.ord;
return ord(char) >= ord('0')
and ord(char) <= ord('9');
}
+80
View File
@@ -0,0 +1,80 @@
request = import("request.noja");
response = import("response.noja");
Server = {
fd: int,
callback: Callable,
loop : Callable,
close: Callable
};
fun handleClient(fd: int, server: Server) {
req, error, error_is_internal = request.fromSocket(fd);
if error != none: {
if error_is_internal: {
if debug:
res = response.new(500, error);
else
res = response.new(500);
} else
res = response.new(400, error);
} else {
if req.version.major != 1:
res = response.new(505, "HTTP version not supported");
else
res = server.callback(req);
}
return response.toSocket(fd, res);
}
fun fallback_callback(request: Request)
return response.new(500, "No callback provided");
return {
Server: Server,
fun new(callback: Callable = fallback_callback,
addr : String = "127.0.0.1",
port : int = 8080,
backlog : int = 32) {
fd, err = net.socket(net.AF_INET, net.SOCK_STREAM, 0);
if err != none:
return none, err;
if (err = net.bind(fd, net.AF_INET, port, addr)) != none: {
net.close(fd);
return none, err;
}
if (err = net.listen(fd, backlog)) != none: {
net.close(fd);
return none, err;
}
return {
fd: fd,
callback: callback,
fun loop(server: Server) {
while true: {
fd, addr_or_err, port = net.accept(server.fd);
if fd == none:
error = addr_or_err;
else {
error = handleClient(fd, server);
net.close(fd);
}
if error != none:
print(error);
}
}
fun close(server: Server)
net.close(server.fd);
};
}
};
+3 -3
View File
@@ -11,9 +11,9 @@ Scanner = {
fun newScanner(src: String) { fun newScanner(src: String) {
fun isSpace(c: String) fun isSpace(c: String)
return c == ' ' return c == ' '
or c == '\t' or c == '\t'
or c == '\n'; or c == '\n';
scan = { scan = {
src: src, src: src,
+1 -1
View File
@@ -11,7 +11,7 @@ while i < n: {
i = i + 1; i = i + 1;
fun printPercent() fun printPercent()
print(100.0 * i / n, '%\n'); print(string.cat('\r', stringFromNumeric(100.0 * i / n), '%'));
printPercent(); printPercent();
} }
+46 -92
View File
@@ -32,7 +32,9 @@
#include <string.h> #include <string.h>
#include <stdint.h> #include <stdint.h>
#include <unistd.h> #include <unistd.h>
#include "net.h"
#include "math.h" #include "math.h"
#include "utils.h"
#include "basic.h" #include "basic.h"
#include "files.h" #include "files.h"
#include "string.h" #include "string.h"
@@ -43,6 +45,21 @@
#include "../runtime/runtime.h" #include "../runtime/runtime.h"
#include "../compiler/compile.h" #include "../compiler/compile.h"
static int bin_typename(Runtime *runtime, Object **argv, unsigned int argc, Object *rets[static MAX_RETS], Error *error)
{
UNUSED(argc);
ASSERT(argc == 1);
Heap *heap = Runtime_GetHeap(runtime);
const char *name = argv[0]->type->name;
Object *o_name = Object_FromString(name, -1, heap, error);
if (o_name == NULL)
return -1;
rets[0] = o_name;
return 1;
}
static int bin_print(Runtime *runtime, Object **argv, unsigned int argc, Object *rets[static MAX_RETS], Error *error) static int bin_print(Runtime *runtime, Object **argv, unsigned int argc, Object *rets[static MAX_RETS], Error *error)
{ {
UNUSED(runtime); UNUSED(runtime);
@@ -54,29 +71,21 @@ static int bin_print(Runtime *runtime, Object **argv, unsigned int argc, Object
return 0; return 0;
} }
static int bin_import(Runtime *runtime, Object **argv, unsigned int argc, Object *rets[static MAX_RETS], Error *error) static int bin_import(Runtime *runtime,
Object **argv,
unsigned int argc,
Object *rets[static MAX_RETS],
Error *error)
{ {
UNUSED(argc); UNUSED(argc);
ASSERT(argc == 1); ASSERT(argc == 1);
Heap *heap = Runtime_GetHeap(runtime); ParsedArgument pargs[2];
ASSERT(heap != NULL); if (!parseArgs(error, argv, argc, pargs, "s"))
return -1;
Object *o_path = argv[0]; const char *path = pargs[0].as_string.data;
const char *path; size_t path_len = pargs[0].as_string.size;
size_t path_len;
{
if(!Object_IsString(o_path))
{
Error_Report(error, 0, "Argument #%d is not a string", 1);
return -1;
}
path = Object_GetString(o_path, &path_len);
if (path == NULL)
return -1;
}
char full_path[1024]; char full_path[1024];
@@ -102,87 +111,27 @@ static int bin_import(Runtime *runtime, Object **argv, unsigned int argc, Object
full_path[written + path_len] = '\0'; full_path[written + path_len] = '\0';
} }
Error sub_error; Source *src = Source_FromFile(full_path, error);
Error_Init(&sub_error); if(src == NULL)
Source *src = Source_FromFile(full_path, &sub_error); return -1;
if(src == NULL) {
Object *o_none = Object_NewNone(heap, error);
if(o_none == NULL)
return -1;
Object *o_err = Object_FromString(sub_error.message, -1, heap, error);
if(o_err == NULL)
return -1;
Error_Free(&sub_error);
rets[0] = o_none;
rets[1] = o_err;
return 2;
}
CompilationErrorType errtyp; CompilationErrorType errtyp;
Executable *exe = compile(src, &sub_error, &errtyp); Executable *exe = compile(src, error, &errtyp);
if(exe == NULL) { if(exe == NULL) {
const char *errname;
switch(errtyp) {
default:
case CompilationErrorType_INTERNAL: errname = NULL; break;
case CompilationErrorType_SYNTAX: errname = "Syntax"; break;
case CompilationErrorType_SEMANTIC: errname = "Semantic"; break;
}
UNUSED(errname);
Object *o_none = Object_NewNone(heap, error);
if(o_none == NULL) {
Source_Free(src);
return -1;
}
Object *o_err = Object_FromString(sub_error.message, -1, heap, error);
if(o_err == NULL) {
Source_Free(src);
return -1;
}
Source_Free(src); Source_Free(src);
Error_Free(&sub_error); return -1;
rets[0] = o_none;
rets[1] = o_err;
return 2;
} }
Object *sub_rets[8]; Object *sub_rets[8];
int retc = run(runtime, &sub_error, exe, 0, NULL, NULL, 0, sub_rets); int retc = run(runtime, error, exe, 0, NULL, NULL, 0, sub_rets);
if(retc < 0) if(retc < 0)
{ {
const char *errname = "Runtime";
// Snapshot?
UNUSED(errname);
Object *o_none = Object_NewNone(heap, error);
if(o_none == NULL) {
Source_Free(src);
Executable_Free(exe);
return -1;
}
Object *o_err = Object_FromString(sub_error.message, -1, heap, error);
if(o_err == NULL) {
Source_Free(src);
Executable_Free(exe);
return -1;
}
Error_Free(&sub_error);
rets[0] = o_none;
rets[1] = o_err;
Source_Free(src); Source_Free(src);
Executable_Free(exe); Executable_Free(exe);
return 2; return -1;
} }
ASSERT(retc == 1); ASSERT(retc == 1);
Error_Free(&sub_error);
Source_Free(src); Source_Free(src);
Executable_Free(exe); Executable_Free(exe);
rets[0] = sub_rets[0]; rets[0] = sub_rets[0];
@@ -355,13 +304,14 @@ void bins_basic_init(StaticMapSlot slots[])
slots[3].as_type = Object_GetBoolType(); slots[3].as_type = Object_GetBoolType();
slots[4].as_type = Object_GetFloatType(); slots[4].as_type = Object_GetFloatType();
slots[5].as_type = Object_GetStringType(); slots[5].as_type = Object_GetStringType();
slots[6].as_type = Object_GetListType(); slots[6].as_type = Object_GetBufferType();
slots[7].as_type = Object_GetMapType(); slots[7].as_type = Object_GetListType();
slots[8].as_type = Object_GetFileType(); slots[8].as_type = Object_GetMapType();
slots[9].as_type = Object_GetDirType(); slots[9].as_type = Object_GetFileType();
slots[10].as_type = Object_GetNullableType(); slots[10].as_type = Object_GetDirType();
slots[11].as_type = Object_GetSumType(); slots[11].as_type = Object_GetNullableType();
slots[12].as_object = Object_NewAny(); slots[12].as_type = Object_GetSumType();
slots[13].as_object = Object_NewAny();
} }
StaticMapSlot bins_basic[] = { StaticMapSlot bins_basic[] = {
@@ -371,6 +321,7 @@ StaticMapSlot bins_basic[] = {
{ TYPENAME_BOOL, SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ }, { TYPENAME_BOOL, SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ },
{ TYPENAME_FLOAT, SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ }, { TYPENAME_FLOAT, SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ },
{ TYPENAME_STRING, SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ }, { TYPENAME_STRING, SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ },
{ TYPENAME_BUFFER, SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ },
{ TYPENAME_LIST, SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ }, { TYPENAME_LIST, SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ },
{ TYPENAME_MAP, SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ }, { TYPENAME_MAP, SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ },
{ TYPENAME_FILE, SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ }, { TYPENAME_FILE, SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ },
@@ -378,6 +329,8 @@ StaticMapSlot bins_basic[] = {
{ TYPENAME_NULLABLE, SM_TYPE, .as_type = NULL }, { TYPENAME_NULLABLE, SM_TYPE, .as_type = NULL },
{ TYPENAME_SUM, SM_TYPE, .as_type = NULL }, { TYPENAME_SUM, SM_TYPE, .as_type = NULL },
{ "any", SM_OBJECT, .as_object = NULL }, { "any", SM_OBJECT, .as_object = NULL },
{ "net", SM_SMAP, .as_smap = bins_net, },
{ "math", SM_SMAP, .as_smap = bins_math, }, { "math", SM_SMAP, .as_smap = bins_math, },
{ "files", SM_SMAP, .as_smap = bins_files, }, { "files", SM_SMAP, .as_smap = bins_files, },
{ "buffer", SM_SMAP, .as_smap = bins_buffer, }, { "buffer", SM_SMAP, .as_smap = bins_buffer, },
@@ -391,6 +344,7 @@ StaticMapSlot bins_basic[] = {
{ "error", SM_FUNCT, .as_funct = bin_error, .argc = 1 }, { "error", SM_FUNCT, .as_funct = bin_error, .argc = 1 },
{ "assert", SM_FUNCT, .as_funct = bin_assert, .argc = -1 }, { "assert", SM_FUNCT, .as_funct = bin_assert, .argc = -1 },
{ "istypeof", SM_FUNCT, .as_funct = bin_istypeof, .argc = 2, }, { "istypeof", SM_FUNCT, .as_funct = bin_istypeof, .argc = 2, },
{ "typename", SM_FUNCT, .as_funct = bin_typename, .argc = 1, },
{ "keysof", SM_FUNCT, .as_funct = bin_keysof, .argc = 1, }, { "keysof", SM_FUNCT, .as_funct = bin_keysof, .argc = 1, },
{ NULL, SM_END, {}, {} }, { NULL, SM_END, {}, {} },
}; };
+24 -2
View File
@@ -1,5 +1,7 @@
#include "buffer.h" #include "buffer.h"
#include "utils.h"
#include "../utils/defs.h" #include "../utils/defs.h"
#include "../utils/utf8.h"
#include "../runtime/runtime.h" #include "../runtime/runtime.h"
static int bin_new(Runtime *runtime, Object **argv, unsigned int argc, Object *rets[static MAX_RETS], Error *error) static int bin_new(Runtime *runtime, Object **argv, unsigned int argc, Object *rets[static MAX_RETS], Error *error)
@@ -53,6 +55,22 @@ static int bin_sliceUp(Runtime *runtime, Object **argv, unsigned int argc, Objec
return 1; return 1;
} }
static int bin_fromString(Runtime *runtime, Object **argv, unsigned int argc, Object *rets[static MAX_RETS], Error *error)
{
ASSERT(argc == 1);
ParsedArgument pargs[1];
if (!parseArgs(error, argv, argc, pargs, "s"))
return -1;
const char *str = pargs[0].as_string.data;
size_t len = pargs[0].as_string.size;
Object *buffer = Object_NewBufferFromString(str, len, Runtime_GetHeap(runtime), error);
if (buffer == NULL)
return -1;
rets[0] = buffer;
return 1;
}
static int bin_toString(Runtime *runtime, Object **argv, unsigned int argc, Object *rets[static MAX_RETS], Error *error) static int bin_toString(Runtime *runtime, Object **argv, unsigned int argc, Object *rets[static MAX_RETS], Error *error)
{ {
UNUSED(argc); UNUSED(argc);
@@ -70,6 +88,9 @@ static int bin_toString(Runtime *runtime, Object **argv, unsigned int argc, Obje
buffaddr = Object_GetBuffer(argv[0], &buffsize); buffaddr = Object_GetBuffer(argv[0], &buffsize);
ASSERT(buffaddr != NULL); ASSERT(buffaddr != NULL);
if(utf8_strlen(buffaddr, buffsize) < 0)
return returnValues2(error, runtime, rets, "ns", "Buffer doesn't contain valid UTF-8");
Object *temp = Object_FromString(buffaddr, buffsize, Runtime_GetHeap(runtime), error); Object *temp = Object_FromString(buffaddr, buffsize, Runtime_GetHeap(runtime), error);
if(temp == NULL) if(temp == NULL)
@@ -80,7 +101,8 @@ static int bin_toString(Runtime *runtime, Object **argv, unsigned int argc, Obje
} }
StaticMapSlot bins_buffer[] = { StaticMapSlot bins_buffer[] = {
{ "new", SM_FUNCT, .as_funct = bin_new, .argc = 1 }, { "new", SM_FUNCT, .as_funct = bin_new, .argc = 1 },
{ "sliceUp", SM_FUNCT, .as_funct = bin_sliceUp, .argc = 3 }, { "sliceUp", SM_FUNCT, .as_funct = bin_sliceUp, .argc = 3 },
{ "toString", SM_FUNCT, .as_funct = bin_toString, .argc = 1 }, { "toString", SM_FUNCT, .as_funct = bin_toString, .argc = 1 },
{ "fromString", SM_FUNCT, .as_funct = bin_fromString, .argc = 1 },
}; };
+54 -147
View File
@@ -30,6 +30,7 @@
#include <errno.h> #include <errno.h>
#include "files.h" #include "files.h"
#include "utils.h"
#include "../utils/defs.h" #include "../utils/defs.h"
enum { enum {
@@ -38,30 +39,19 @@ enum {
MD_APPEND = 2, MD_APPEND = 2,
}; };
#include <errno.h>
static int bin_openFile(Runtime *runtime, Object **argv, unsigned int argc, Object *rets[static MAX_RETS], Error *error) static int bin_openFile(Runtime *runtime, Object **argv, unsigned int argc, Object *rets[static MAX_RETS], Error *error)
{ {
UNUSED(argc); UNUSED(argc);
ASSERT(argc == 2); ASSERT(argc == 2);
if(!Object_IsString(argv[0]))
{
Error_Report(error, 0, "Expected first argument to be a string, but it's a %s", Object_GetName(argv[0]));
return -1;
}
if(!Object_IsInt(argv[1]))
{
Error_Report(error, 0, "Expected second argument to be an int, but it's a %s", Object_GetName(argv[1]));
return -1;
}
Heap *heap = Runtime_GetHeap(runtime); Heap *heap = Runtime_GetHeap(runtime);
int mode = Object_GetInt(argv[1]); ParsedArgument pargs[2];
const char *path = Object_GetString(argv[0], NULL); if (!parseArgs(error, argv, argc, pargs, "si"))
ASSERT(path != NULL); // We know argv[0] is a string. return -1;
const char *path = pargs[0].as_string.data;
int mode = pargs[1].as_int;
FILE *fp; FILE *fp;
{ {
@@ -91,39 +81,23 @@ static int bin_openFile(Runtime *runtime, Object **argv, unsigned int argc, Obje
fp = fopen(path, mode2); fp = fopen(path, mode2);
if(fp == NULL) { if(fp == NULL) {
Object *o_none = Object_NewNone(heap, error);
if(o_none == NULL)
return -1;
const char *errdesc; const char *errdesc;
switch(errno) { switch(errno) {
case EACCES: errdesc = "Can't access file"; break; case EACCES: errdesc = "Can't access file"; break;
case EPERM: errdesc = "Permission denied"; break; case EPERM: errdesc = "Permission denied"; break;
case EEXIST: errdesc = "File or folder already exists"; break; case EEXIST: errdesc = "File or folder already exists"; break;
case EISDIR: errdesc = "Entity is a directory"; break; case EISDIR: errdesc = "Entity is a directory"; break;
case ENOTDIR: errdesc = "Entity is not a directory"; break; case ENOTDIR: errdesc = "Entity is not a directory"; break;
case ELOOP: errdesc = "Too many symbolic links"; break; case ELOOP: errdesc = "Too many symbolic links"; break;
case ENAMETOOLONG: errdesc = "Entity name is too long"; break; case ENAMETOOLONG: errdesc = "Entity name is too long"; break;
case ENFILE: errdesc = "Open descriptors limit reached"; break; case ENFILE: errdesc = "Open descriptors limit reached"; break;
case ENOENT: errdesc = "File or folder doesn't exist"; break; case ENOENT: errdesc = "File or folder doesn't exist"; break;
default: errdesc = "Unexpected error"; break; default: errdesc = "Unexpected error"; break;
} }
return returnValues(error, heap, rets, "ns", errdesc);
Object *o_error = Object_FromString(errdesc, -1, heap, error);
if(o_error == NULL)
return -1;
rets[0] = o_none;
rets[1] = o_error;
return 2;
} }
} }
return returnValues(error, heap, rets, "F", fp);
rets[0] = Object_FromStream(fp, heap, error);
if(rets[0] == NULL)
return -1;
return 1;
} }
static int bin_read(Runtime *runtime, Object **argv, unsigned int argc, Object *rets[static MAX_RETS], Error *error) static int bin_read(Runtime *runtime, Object **argv, unsigned int argc, Object *rets[static MAX_RETS], Error *error)
@@ -135,62 +109,31 @@ static int bin_read(Runtime *runtime, Object **argv, unsigned int argc, Object *
// Arg 1: buffer // Arg 1: buffer
// Arg 2: count // Arg 2: count
if(!Object_IsFile(argv[0])) ParsedArgument pargs[3];
{ if (!parseArgs(error, argv, argc, pargs, "FB?i"))
Error_Report(error, 0, "Expected first argument to be a file, but it's a %s", Object_GetName(argv[0]));
return -1; return -1;
}
if(!Object_IsBuffer(argv[1])) FILE *stream;
{ void *dstptr;
Error_Report(error, 0, "Expected second argument to be a buffer, but it's a %s", Object_GetName(argv[1])); size_t dstlen;
return -1; size_t count;
}
Heap *heap = Runtime_GetHeap(runtime); stream = pargs[0].as_file;
dstptr = pargs[1].as_buffer.data;
void *buff_addr; dstlen = pargs[1].as_buffer.size;
size_t buff_size; if (pargs[2].defined) {
int n = pargs[2].as_int;
buff_addr = Object_GetBuffer(argv[1], &buff_size); if (n < 0) {
Error_Report(error, 0, "Argument count must be a non-negative integer");
size_t read_size;
if(Object_IsNone(argv[2]))
{
read_size = buff_size;
}
else if(Object_IsInt(argv[2]))
{
long long int temp = Object_GetInt(argv[2]);
if(temp < 0)
{
Error_Report(error, 0, "Expected third argument to be a positive integer");
return -1; return -1;
} }
count = MIN((size_t) n, dstlen);
} else
count = (size_t) dstlen;
read_size = (size_t) temp; // TODO: Handle potential overflow. size_t n = fread(dstptr, 1, count, stream);
if(read_size > buff_size) return returnValues2(error, runtime, rets, "i", n);
read_size = buff_size;
}
else
{
Error_Report(error, 0, "Expected third argument to be an int or none, but it's a %s", Object_GetName(argv[0]));
return -1;
}
FILE *fp = Object_GetStream(argv[0]);
if(fp == NULL)
return -1;
size_t n = fread(buff_addr, 1, read_size, fp);
rets[0] = Object_FromInt(n, heap, error);
if(rets[0] == NULL)
return -1;
return 1;
} }
static int bin_write(Runtime *runtime, Object **argv, unsigned int argc, Object *rets[static MAX_RETS], Error *error) static int bin_write(Runtime *runtime, Object **argv, unsigned int argc, Object *rets[static MAX_RETS], Error *error)
@@ -202,62 +145,31 @@ static int bin_write(Runtime *runtime, Object **argv, unsigned int argc, Object
// Arg 1: buffer // Arg 1: buffer
// Arg 2: count // Arg 2: count
if(!Object_IsFile(argv[0])) ParsedArgument pargs[3];
{ if (!parseArgs(error, argv, argc, pargs, "FB?i"))
Error_Report(error, 0, "Expected first argument to be a file, but it's a %s", Object_GetName(argv[0]));
return -1; return -1;
}
if(!Object_IsBuffer(argv[1])) FILE *stream;
{ void *srcptr;
Error_Report(error, 0, "Expected second argument to be a buffer, but it's a %s", Object_GetName(argv[1])); size_t srclen;
return -1; size_t count;
}
Heap *heap = Runtime_GetHeap(runtime); stream = pargs[0].as_file;
srcptr = pargs[1].as_buffer.data;
void *buff_addr; srclen = pargs[1].as_buffer.size;
size_t buff_size; if (pargs[2].defined) {
int n = pargs[2].as_int;
buff_addr = Object_GetBuffer(argv[1], &buff_size); if (n < 0) {
Error_Report(error, 0, "Argument count must be a non-negative integer");
size_t write_size;
if(Object_IsNone(argv[2]))
{
write_size = buff_size;
}
else if(Object_IsInt(argv[2]))
{
long long int temp = Object_GetInt(argv[2]);
if(temp < 0)
{
Error_Report(error, 0, "Expected third argument to be a positive integer");
return -1; return -1;
} }
count = MIN((size_t) n, srclen);
} else
count = (int) srclen;
write_size = (size_t) temp; // TODO: Handle potential overflow. size_t n = fwrite(srcptr, 1, count, stream);
if(write_size > buff_size) return returnValues2(error, runtime, rets, "i", n);
write_size = buff_size;
}
else
{
Error_Report(error, 0, "Expected third argument to be an int or none, but it's a %s", Object_GetName(argv[0]));
return -1;
}
FILE *fp = Object_GetStream(argv[0]);
if(fp == NULL)
return -1;
size_t n = fwrite(buff_addr, 1, write_size, fp);
rets[0] = Object_FromInt(n, heap, error);
if(rets[0] == NULL)
return -1;
return 1;
} }
static int bin_openDir(Runtime *runtime, Object **argv, unsigned int argc, Object *rets[static MAX_RETS], Error *error) static int bin_openDir(Runtime *runtime, Object **argv, unsigned int argc, Object *rets[static MAX_RETS], Error *error)
@@ -311,8 +223,6 @@ static int bin_nextDirItem(Runtime *runtime, Object **argv, unsigned int argc, O
DIR *dir = Object_GetDIR(argv[0]); DIR *dir = Object_GetDIR(argv[0]);
ASSERT(dir != NULL); ASSERT(dir != NULL);
Heap *heap = Runtime_GetHeap(runtime);
errno = 0; errno = 0;
struct dirent *ent = readdir(dir); struct dirent *ent = readdir(dir);
@@ -327,10 +237,7 @@ static int bin_nextDirItem(Runtime *runtime, Object **argv, unsigned int argc, O
return -1; return -1;
} }
rets[0] = Object_FromString(ent->d_name, -1, heap, error); return returnValues2(error, runtime, rets, "s", ent->d_name);
if(rets[0] == NULL)
return -1;
return 1;
} }
StaticMapSlot bins_files[] = { StaticMapSlot bins_files[] = {
+42 -2
View File
@@ -81,8 +81,6 @@
return 1; \ return 1; \
} }
WRAP_FUNC(ceil)
WRAP_FUNC(floor)
WRAP_FUNC(sin) WRAP_FUNC(sin)
WRAP_FUNC(cos) WRAP_FUNC(cos)
WRAP_FUNC(tan) WRAP_FUNC(tan)
@@ -96,6 +94,48 @@ WRAP_FUNC(sqrt)
WRAP_FUNC_2(atan2) WRAP_FUNC_2(atan2)
WRAP_FUNC_2(pow) WRAP_FUNC_2(pow)
static int bin_ceil(Runtime *runtime, Object **argv, unsigned int argc, Object *rets[static MAX_RETS], Error *error) \
{
UNUSED(argc);
ASSERT(argc == 1);
if(Object_IsFloat(argv[0]))
{
double v = Object_GetFloat(argv[0]);
rets[0] = Object_FromInt(ceil(v), Runtime_GetHeap(runtime), error);
if(rets[0] == NULL)
return -1;
return 1;
}
else
{
Error_Report(error, 0, "Expected first argument to be a float, but it's a %s", Object_GetName(argv[0]));
return -1;
}
}
static int bin_floor(Runtime *runtime, Object **argv, unsigned int argc, Object *rets[static MAX_RETS], Error *error) \
{
UNUSED(argc);
ASSERT(argc == 1);
if(Object_IsFloat(argv[0]))
{
double v = Object_GetFloat(argv[0]);
rets[0] = Object_FromInt(floor(v), Runtime_GetHeap(runtime), error);
if(rets[0] == NULL)
return -1;
return 1;
}
else
{
Error_Report(error, 0, "Expected first argument to be a float, but it's a %s", Object_GetName(argv[0]));
return -1;
}
}
StaticMapSlot bins_math[] = { StaticMapSlot bins_math[] = {
{ "PI", SM_FLOAT, .as_float = M_PI }, { "PI", SM_FLOAT, .as_float = M_PI },
{ "E", SM_FLOAT, .as_float = M_E }, { "E", SM_FLOAT, .as_float = M_E },
+195
View File
@@ -0,0 +1,195 @@
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include "net.h"
#include "utils.h"
#include "../utils/defs.h"
static int bin_socket(Runtime *runtime, Object **argv, unsigned int argc, Object *rets[static MAX_RETS], Error *error)
{
ASSERT(argc == 3);
ParsedArgument pargs[3];
if (!parseArgs(error, argv, argc, pargs, "iii"))
return -1;
int domain = pargs[0].as_int;
int type = pargs[1].as_int;
int proto = pargs[2].as_int;
int fd = socket(domain, type, proto);
if (fd < 0)
return returnValues2(error, runtime, rets, "ns", strerror(errno));
return returnValues2(error, runtime, rets, "i", fd);
}
static int bin_bind(Runtime *runtime, Object **argv, unsigned int argc, Object *rets[static MAX_RETS], Error *error)
{
// 0: fd
// 1: family
// 2: port (family=AF_INET)
// 3: addr (family=AF_INET)
ASSERT(argc == 4);
ParsedArgument pargs[4];
if (!parseArgs(error, argv, argc, pargs, "iii?s"))
return -1;
int fd = pargs[0].as_int;
int family = pargs[1].as_int;
if (family != AF_INET)
return returnValues2(error, runtime, rets, "ns", "Invalid family (only AF_INET is supported at the moment)");
int port = pargs[2].as_int;
const char *addr = pargs[3].defined ? pargs[3].as_string.data : NULL;
struct in_addr parsed_addr;
if (addr == NULL)
parsed_addr.s_addr = INADDR_ANY;
else {
int res = inet_pton(family, addr, &parsed_addr);
if (res == 0) return returnValues2(error, runtime, rets, "ns", "Invalid address");
if (res < 0) return returnValues2(error, runtime, rets, "ns", "Invalid family");
}
struct sockaddr_in buff;
buff.sin_family = family;
buff.sin_port = htons(port);
buff.sin_addr = parsed_addr;
if (bind(fd, (struct sockaddr*) &buff, sizeof(buff)) < 0)
return returnValues2(error, runtime, rets, "s", strerror(errno));
return returnValues2(error, runtime, rets, "n");
}
static int bin_listen(Runtime *runtime, Object **argv, unsigned int argc, Object *rets[static MAX_RETS], Error *error)
{
// 0: fd
// 1: backlog
ASSERT(argc == 2);
ParsedArgument pargs[2];
if (!parseArgs(error, argv, argc, pargs, "ii"))
return -1;
int fd = pargs[0].as_int;
int backlog = pargs[1].as_int;
if (listen(fd, backlog) < 0)
return returnValues2(error, runtime, rets, "s", strerror(errno));
return returnValues2(error, runtime, rets, "n");
}
static int bin_recv(Runtime *runtime, Object **argv, unsigned int argc, Object *rets[static MAX_RETS], Error *error)
{
// 0: fd
// 1: buffer
// 2: count
ASSERT(argc == 3);
ParsedArgument pargs[3];
if (!parseArgs(error, argv, argc, pargs, "iB?i"))
return -1;
int fd = pargs[0].as_int;
void *dstptr = pargs[1].as_buffer.data;
size_t dstlen = pargs[1].as_buffer.size;
size_t count;
if (pargs[2].defined) {
int n = pargs[2].as_int;
if (n < 0)
return returnValues2(error, runtime, rets, "ns", "Invalid negative count");
count = MIN((size_t) n, dstlen);
} else
count = dstlen;
ssize_t res = recv(fd, dstptr, count, 0);
if (res < 0)
return returnValues2(error, runtime, rets, "ns", strerror(errno));
return returnValues2(error, runtime, rets, "i", res);
}
static int bin_send(Runtime *runtime, Object **argv, unsigned int argc, Object *rets[static MAX_RETS], Error *error)
{
// 0: fd
// 1: buffer
// 2: count
ASSERT(argc == 3);
ParsedArgument pargs[3];
if (!parseArgs(error, argv, argc, pargs, "iB?i"))
return -1;
int fd = pargs[0].as_int;
void *srcptr = pargs[1].as_buffer.data;
size_t srclen = pargs[1].as_buffer.size;
size_t count;
if (pargs[2].defined) {
int n = pargs[2].as_int;
if (n < 0)
return returnValues2(error, runtime, rets, "ns", "Invalid negative count");
count = MIN((size_t) n, srclen);
} else
count = srclen;
ssize_t res = send(fd, srcptr, count, 0);
if (res < 0)
return returnValues2(error, runtime, rets, "ns", strerror(errno));
return returnValues2(error, runtime, rets, "i", res);
}
static int bin_accept(Runtime *runtime, Object **argv, unsigned int argc, Object *rets[static MAX_RETS], Error *error)
{
// 0: fd
ASSERT(argc == 1);
ParsedArgument pargs[1];
if (!parseArgs(error, argv, argc, pargs, "i"))
return -1;
int fd = pargs[0].as_int;
struct sockaddr_in new_addr;
socklen_t new_addr_size;
int new_fd = accept(fd, (struct sockaddr*) &new_addr, &new_addr_size);
if (new_fd < 0)
return returnValues2(error, runtime, rets, "ns", strerror(errno));
ASSERT(new_addr.sin_family == AF_INET);
ASSERT(new_addr_size == sizeof(struct sockaddr_in));
return returnValues2(error, runtime, rets, "iii", new_fd, new_addr.sin_addr.s_addr, new_addr.sin_port);
}
static int bin_close(Runtime *runtime, Object **argv, unsigned int argc, Object *rets[static MAX_RETS], Error *error)
{
// 0: fd
ASSERT(argc == 1);
ParsedArgument pargs[1];
if (!parseArgs(error, argv, argc, pargs, "i"))
return -1;
int fd = pargs[0].as_int;
int res = close(fd);
return returnValues2(error, runtime, rets, "i", res);
}
StaticMapSlot bins_net[] = {
{ "AF_INET", SM_INT, .as_int = AF_INET, },
{ "SOCK_STREAM", SM_INT, .as_int = SOCK_STREAM, },
{ "SOCK_DGRAM", SM_INT, .as_int = SOCK_DGRAM, },
{ "socket", SM_FUNCT, .as_funct = bin_socket, .argc = 3, },
{ "bind", SM_FUNCT, .as_funct = bin_bind, .argc = 4, },
{ "listen", SM_FUNCT, .as_funct = bin_listen, .argc = 2, },
{ "accept", SM_FUNCT, .as_funct = bin_accept, .argc = 1, },
{ "recv", SM_FUNCT, .as_funct = bin_recv, .argc = 3, },
{ "send", SM_FUNCT, .as_funct = bin_send, .argc = 3, },
{ "close", SM_FUNCT, .as_funct = bin_close, .argc = 1, },
{ NULL, SM_END, {}, {} },
};
+2
View File
@@ -0,0 +1,2 @@
#include "../runtime/runtime.h"
extern StaticMapSlot bins_net[];
+168 -40
View File
@@ -1,53 +1,41 @@
fun abs(n: Numeric) { fun dummy() {}
if n < 0:
return -n;
return n;
}
fun min(x, y) { Func = type(dummy);
if x < y:
return x;
return y;
}
fun max(x, y) {
if x > y:
return x;
return y;
}
Func = type(abs);
NFunc = type(print); NFunc = type(print);
Numeric = int | float; Numeric = int | float;
Callable = Func | NFunc; Callable = Func | NFunc;
Collection = List | Map; Collection = List | Map;
chr = string.chr;
ord = string.ord;
cat = string.cat;
# Stringify a number between 0 and 9. # Stringify a number between 0 and 9.
fun stringFromDigit(digit: int) { fun stringFromDigit(digit: int) {
assert(digit >= 0 and digit <= 9); assert(digit >= 0 and digit <= 9);
return string.chr(string.ord("0") + digit); return chr(ord("0") + digit);
}
# This function returns the magnitude of the
# argument and the power of 10 with the same
# magnitude.
fun getIntegerMagnitude(n: int) {
assert(n >= 0);
magn = 0;
powr = 1;
# Basically grow the power until it's
# bigger than the input.
while n >= powr * 10: {
powr = 10 * powr;
magn = 1 + magn;
}
return magn, powr;
} }
# Stringify an unsigned integer # Stringify an unsigned integer
fun stringFromInteger(n: int) { fun stringFromInteger(n: int) {
# This function returns the magnitude of the
# argument and the power of 10 with the same
# magnitude.
fun getIntegerMagnitude(n: int) {
assert(n >= 0);
magn = 0;
powr = 1;
# Basically grow the power until it's
# bigger than the input.
while n >= powr * 10: {
powr = 10 * powr;
magn = 1 + magn;
}
return magn, powr;
}
negative = (n < 0); negative = (n < 0);
# Get the power of 10 with the # Get the power of 10 with the
@@ -63,27 +51,167 @@ fun stringFromInteger(n: int) {
temp = temp % power; # Remove it temp = temp % power; # Remove it
# Add the digit to the output. # Add the digit to the output.
text = string.cat(text, stringFromDigit(digit)); text = cat(text, stringFromDigit(digit));
# Prep for the next iteration. # Prep for the next iteration.
power = power / 10; power = power / 10;
} }
if negative: if negative:
text = string.cat("-", text); text = cat("-", text);
return text; return text;
} }
fun stringFromFloating(f: float, max_decimals: int = 2) {
# This function is just a fast hack
epsilon = 0.00000001;
asint = math.floor(f);
text = stringFromInteger(asint);
text = cat(text, ".");
magn = 1.0;
temp = f - asint;
i = 0;
do {
magn = magn / 10;
digit = math.floor(temp / magn);
text = cat(text, stringFromDigit(digit));
temp = temp - magn * digit; # Remove the handled digit
i = i+1;
} while temp > epsilon and i < max_decimals;
return text;
}
fun stringFromNumeric(num: Numeric) {
if type(num) == int:
return stringFromInteger(num);
return stringFromFloating(num);
}
fun floatFromInteger(n: int) fun floatFromInteger(n: int)
return 1.0 * n; return 1.0 * n;
fun stringFromList(list: List) {
s = "[";
i = 0;
while i < count(list): {
s = cat(s, toString(list[i]));
i = i+1;
if i < count(list):
s = cat(s, ", ");
}
s = cat(s, "]");
return s;
}
fun stringFromMap(map: Map, can_use_method=true) {
s = none; # Result
has_method = istypeof(Callable, map.toString);
if can_use_method and has_method:
s = map->toString();
else {
s = "{";
i = 0;
keys = keysof(map);
while i < count(keys): {
s = cat(s, toString(keys[i]), ": ", toString(map[keys[i]]));
i = i+1;
if i < count(keys):
s = cat(s, ", ");
}
s = cat(s, "}");
}
return s;
}
fun integerFromDigit(char: String) { fun integerFromDigit(char: String) {
ord = string.ord;
res = ord(char) - ord('0'); res = ord(char) - ord('0');
if res < 0 or res > 9: if res < 0 or res > 9:
error("String isn't a digit"); error("String isn't a digit");
return res; return res;
} }
fun append(array: List, item) fun GenericIterator(T) return {
array[count(array)] = item; set : T,
keys : List,
index: ?int,
next : Callable
};
fun makeGenericIterator(set: Collection) return {
set : set,
keys : keysof(set),
index: none,
fun next(iter: GenericIterator(type(set))) {
index = iter.index;
if index == none:
index = 0;
else {
if index < count(iter.keys):
index = index+1;
}
if index == count(iter.keys):
return none;
iter.index = index;
key = iter.keys[index];
val = iter.set[key];
return val, key, index;
}
};
return {
Func: Func,
NFunc: NFunc,
Numeric: Numeric,
Callable: Callable,
Collection: Collection,
fun makeIterator(set: Collection, can_use_method=true) {
if can_use_method and type(set) == Map and istypeof(Callable, set.iter):
return set->iter();
return makeGenericIterator(set);
}
fun toString(value, can_use_method=true) {
T = type(value);
if T == None : return "none";
if T == int : return stringFromInteger(value);
if T == float: return stringFromFloating(value);
if T == List : return stringFromList(value);
if T == Map : return stringFromMap(value, can_use_method);
if T == Type : return typename(T);
if T == Func : return "Func";
if T == NFunc: return "NFunc";
if T == String: return value;
error(cat("Don't know how to convert ", typename(value), " to a string"));
}
fun isCallable(x)
return istypeof(Callable, x);
fun append(array: List, item)
array[count(array)] = item;
fun abs(n: Numeric) {
if n < 0:
return -n;
return n;
}
fun min(x, y) {
if x < y:
return x;
return y;
}
fun max(x, y) {
if x > y:
return x;
return y;
}
};
+174
View File
@@ -0,0 +1,174 @@
#include "utils.h"
int returnValuesVA(Error *error, Heap *heap, Object *rets[static MAX_RETS], const char *fmt, va_list va)
{
int retc = 0, i = 0;
while (fmt[i] != '\0') {
if (retc == MAX_RETS) {
Error_Report(error, 1, "Return value limit reached");
return -1;
}
Object *ret;
switch (fmt[i]) {
case 'o': ret = va_arg(va, Object*); break;
case 'n': ret = Object_NewNone(heap, error); break;
case 'i': ret = Object_FromInt (va_arg(va, int), heap, error); break;
case 'f': ret = Object_FromFloat(va_arg(va, double), heap, error); break;
case 's': ret = Object_FromString(va_arg(va, char*), -1, heap, error); break;
default:
Error_Report(error, 1, "Invalid format specifier '%c'", fmt[i]);
return -1;
}
if (ret == NULL)
return -1;
rets[retc++] = ret;
i++;
}
return retc;
}
bool parseArgs(Error *error,
Object **argv,
unsigned int argc,
ParsedArgument *pargs,
const char *fmt)
{
unsigned int current_arg = 0;
int i = 0;
while (fmt[i] != '\0') {
if (current_arg == argc) {
Error_Report(error, 0, "Missing arguments");
return false;
}
Object *arg = argv[current_arg];
bool may_be_none = false;
if (fmt[i] == '?') {
may_be_none = true;
i++;
if (fmt[i] == '\0') {
Error_Report(error, 1, "Format terminated unexpectedly");
return false;
}
}
if (may_be_none && Object_IsNone(arg)) {
pargs[current_arg].defined = false;
} else {
switch (fmt[i]) {
case 'o': /* Any object */
pargs[current_arg].defined = true;
break;
case 'b': /* Boolean */
if (!Object_IsBool(arg)) {
Error_Report(error, 0, "Argument %d was expected to be bool, but a %s was provided", current_arg+1, arg->type->name);
return false;
}
pargs[current_arg].defined = true;
pargs[current_arg].as_bool = Object_GetBool(arg);
break;
case 'B': /* Buffer */
{
if (!Object_IsBuffer(arg)) {
Error_Report(error, 0, "Argument %d was expected to be a buffer, but a %s was provided", current_arg+1, arg->type->name);
return false;
}
void *data;
size_t size;
data = Object_GetBuffer(arg, &size);
pargs[current_arg].defined = true;
pargs[current_arg].as_buffer.data = data;
pargs[current_arg].as_buffer.size = size;
break;
}
case 'i': /* Integer */
if (!Object_IsInt(arg)) {
Error_Report(error, 0, "Argument %d was expected to be an int, but a %s was provided", current_arg+1, arg->type->name);
return false;
}
pargs[current_arg].defined = true;
pargs[current_arg].as_int = Object_GetInt(arg);
break;
case 'f': /* Float */
if (!Object_IsFloat(arg)) {
Error_Report(error, 0, "Argument %d was expected to be a float, but a %s was provided", current_arg+1, arg->type->name);
return false;
}
pargs[current_arg].defined = true;
pargs[current_arg].as_float = Object_GetFloat(arg);
break;
case 'l': /* List */
if (!Object_IsList(arg)) {
Error_Report(error, 0, "Argument %d was expected to be a list, but a %s was provided", current_arg+1, arg->type->name);
return false;
}
pargs[current_arg].defined = true;
break;
case 'm': /* Map */
if (!Object_IsMap(arg)) {
Error_Report(error, 0, "Argument %d was expected to be a map, but a %s was provided", current_arg+1, arg->type->name);
return false;
}
break;
case 's': /* String */
{
if (!Object_IsString(arg)) {
Error_Report(error, 0, "Argument %d was expected to be a string, but a %s was provided", current_arg+1, arg->type->name);
return false;
}
const void *data;
size_t size;
data = Object_GetString(arg, &size);
pargs[current_arg].defined = true;
pargs[current_arg].as_string.data = data;
pargs[current_arg].as_string.size = size;
break;
}
default:
Error_Report(error, 1, "Invalid argument parser format specifier");
return false;
}
}
i++;
current_arg++;
}
#ifndef NDEBUG
if (current_arg < argc) {
// Ignoring part of the format string
}
#endif
return true;
}
int returnValues2(Error *error, Runtime *runtime, Object *rets[static MAX_RETS], const char *fmt, ...)
{
va_list va;
va_start(va, fmt);
int retc = returnValuesVA(error, Runtime_GetHeap(runtime), rets, fmt, va);
va_end(va);
return retc;
}
int returnValues(Error *error, Heap *heap, Object *rets[static MAX_RETS], const char *fmt, ...)
{
va_list va;
va_start(va, fmt);
int retc = returnValuesVA(error, heap, rets, fmt, va);
va_end(va);
return retc;
}
+27
View File
@@ -0,0 +1,27 @@
#include <stdint.h>
#include <stdbool.h>
#include "../utils/error.h"
#include "../objects/objects.h"
#include "../runtime/runtime.h"
typedef struct {
bool defined;
union {
struct {
const char *data;
size_t size;
} as_string;
struct {
void *data;
size_t size;
} as_buffer;
bool as_bool;
int64_t as_int;
double as_float;
FILE *as_file;
};
} ParsedArgument;
bool parseArgs(Error *error, Object **argv, unsigned int argc, ParsedArgument *pargs, const char *fmt);
int returnValues (Error *error, Heap *heap, Object *rets[static MAX_RETS], const char *fmt, ...);
int returnValues2(Error *error, Runtime *runtime, Object *rets[static MAX_RETS], const char *fmt, ...);
+12
View File
@@ -76,6 +76,18 @@ _Bool Object_IsBuffer(Object *obj)
return Object_GetType(obj) == Object_GetBufferType(); return Object_GetType(obj) == Object_GetBufferType();
} }
Object *Object_NewBufferFromString(const char *str, size_t len, Heap *heap, Error *error)
{
Object *buffer = Object_NewBuffer(len, heap, error);
if (buffer == NULL)
return NULL;
void *ptr = Object_GetBuffer(buffer, NULL);
memcpy(ptr, str, len);
return buffer;
}
Object *Object_NewBuffer(size_t size, Heap *heap, Error *error) Object *Object_NewBuffer(size_t size, Heap *heap, Error *error)
{ {
// Make the thing. // Make the thing.
+1 -1
View File
@@ -139,7 +139,7 @@ int Object_Call(Object *obj, Object **argv, unsigned int argc, Object *rets[stat
if(type->call == NULL) if(type->call == NULL)
{ {
Error_Report(err, 0, "Object %s doesn't implement %s", Object_GetName(obj), __func__); Error_Report(err, 0, "Object of type %s isn't callable", Object_GetName(obj), __func__);
return -1; return -1;
} }
+1
View File
@@ -120,6 +120,7 @@ Object* Object_NewList2(int num, Object **items, Heap *heap, Error *error);
Object* Object_NewListOfConsecutiveIntegers(int first, int last, Heap *heap, Error *error); Object* Object_NewListOfConsecutiveIntegers(int first, int last, Heap *heap, Error *error);
Object* Object_NewNone(Heap *heap, Error *error); Object* Object_NewNone(Heap *heap, Error *error);
Object* Object_NewBuffer(size_t size, Heap *heap, Error *error); Object* Object_NewBuffer(size_t size, Heap *heap, Error *error);
Object* Object_NewBufferFromString(const char *str, size_t len, Heap *heap, Error *error);
Object* Object_NewClosure(Object *parent, Object *new_map, Heap *heap, Error *error); Object* Object_NewClosure(Object *parent, Object *new_map, Heap *heap, Error *error);
Object* Object_SliceBuffer(Object *obj, size_t offset, size_t length, Heap *heap, Error *error); Object* Object_SliceBuffer(Object *obj, size_t offset, size_t length, Heap *heap, Error *error);
Object* Object_NewNullable(Object *item, Heap *heap, Error *error); Object* Object_NewNullable(Object *item, Heap *heap, Error *error);
+1
View File
@@ -879,6 +879,7 @@ static _Bool step(Runtime *runtime, Error *error)
arg_index = ops[0].as_int; arg_index = ops[0].as_int;
arg_name = ops[1].as_string; arg_name = ops[1].as_string;
ASSERT(arg_name != NULL);
if(runtime->frame->used < 2) if(runtime->frame->used < 2)
{ {
+1 -1
View File
@@ -104,7 +104,7 @@ void _Error_Report2(Error *err, _Bool internal,
} }
else else
{ {
snprintf(temp, p+1, fmt, va2); vsnprintf(temp, p+1, fmt, va2);
err->truncated = 0; err->truncated = 0;
err->message = temp; err->message = temp;
err->length = p; err->length = p;