utility functions for implementing noja functions in c; built-ins for socket management; http server draft in noja
This commit is contained in:
@@ -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
|
||||||
|
};
|
||||||
@@ -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
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
Executable
+109
@@ -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();
|
||||||
@@ -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>
|
||||||
@@ -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};
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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');
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
+4
-4
@@ -9,11 +9,11 @@ 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,
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -46,7 +46,7 @@ static const char usage[] =
|
|||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
assert(argc > 0);
|
assert(argc > 0);
|
||||||
|
|
||||||
if(argc == 1)
|
if(argc == 1)
|
||||||
{
|
{
|
||||||
// $ noja
|
// $ noja
|
||||||
|
|||||||
+49
-95
@@ -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,41 +45,48 @@
|
|||||||
#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);
|
||||||
UNUSED(rets);
|
UNUSED(rets);
|
||||||
UNUSED(error);
|
UNUSED(error);
|
||||||
|
|
||||||
for(int i = 0; i < (int) argc; i += 1)
|
for(int i = 0; i < (int) argc; i += 1)
|
||||||
Object_Print(argv[i], stdout);
|
Object_Print(argv[i], stdout);
|
||||||
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;
|
const char *path = pargs[0].as_string.data;
|
||||||
size_t path_len;
|
size_t path_len = pargs[0].as_string.size;
|
||||||
{
|
|
||||||
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];
|
||||||
|
|
||||||
if(path[0] == '/') {
|
if(path[0] == '/') {
|
||||||
@@ -101,88 +110,28 @@ static int bin_import(Runtime *runtime, Object **argv, unsigned int argc, Object
|
|||||||
memcpy(full_path + written, path, path_len);
|
memcpy(full_path + written, path, path_len);
|
||||||
full_path[written + path_len] = '\0';
|
full_path[written + path_len] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
Error sub_error;
|
|
||||||
Error_Init(&sub_error);
|
|
||||||
Source *src = Source_FromFile(full_path, &sub_error);
|
|
||||||
if(src == NULL) {
|
|
||||||
|
|
||||||
Object *o_none = Object_NewNone(heap, error);
|
Source *src = Source_FromFile(full_path, error);
|
||||||
if(o_none == NULL)
|
if(src == NULL)
|
||||||
return -1;
|
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, {}, {} },
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -12,7 +14,7 @@ static int bin_new(Runtime *runtime, Object **argv, unsigned int argc, Object *r
|
|||||||
Error_Report(error, 0, "Argument is not an int");
|
Error_Report(error, 0, "Argument is not an int");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
long long int size = Object_GetInt(argv[0]);
|
long long int size = Object_GetInt(argv[0]);
|
||||||
|
|
||||||
Object *temp = Object_NewBuffer(size, Runtime_GetHeap(runtime), error);
|
Object *temp = Object_NewBuffer(size, Runtime_GetHeap(runtime), 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 },
|
||||||
};
|
};
|
||||||
|
|||||||
+55
-148
@@ -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)
|
|
||||||
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 returnValues2(error, runtime, rets, "i", n);
|
||||||
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)
|
||||||
@@ -310,9 +222,7 @@ 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
@@ -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 },
|
||||||
|
|||||||
@@ -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, {}, {} },
|
||||||
|
};
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
#include "../runtime/runtime.h"
|
||||||
|
extern StaticMapSlot bins_net[];
|
||||||
+168
-40
@@ -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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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, ...);
|
||||||
@@ -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.
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user