1
1
Fork 0
forked from fun/fun

Compare commits

...

6 commits

39 changed files with 563 additions and 89 deletions

View file

@ -0,0 +1,31 @@
/*
* Example: Simple HTTP server using stdlib net/http_server
*
* Run:
* $ fun examples/blocking/net/http.fun
*
* It will serve files from the ./web directory on http://127.0.0.1:8080
*
* Test with curl in another terminal:
* $ curl -v http://127.0.0.1:8080/
* $ curl -v http://127.0.0.1:8080/index.html
*
* You can also place a .fun script in ./web (e.g. hello.fun) and call it:
* $ curl -v 'http://127.0.0.1:8080/hello.fun?name=Fun'
*/
#include <net/http_server.fun>
// Configure
PORT = 8080
DOC_ROOT = "./examples/data/htdocs/"
print("Starting simple HTTP server example…")
print("Serving '" + DOC_ROOT + "' on http://127.0.0.1:" + to_string(PORT))
print("Press Ctrl+C to stop.")
srv = HTTPServer(PORT)
srv.set_htdocs(DOC_ROOT)
// Start (blocking loop)
srv.start()

370
lib/net/http.fun Normal file
View file

@ -0,0 +1,370 @@
/*
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2026 Johannes Findeisen
* Licensed under the terms of the Apache-2.0 license.
* https://opensource.org/license/apache-2-0
*
* Added: 2026-07-04
*/
#include <io/socket.fun>
#include <strings.fun>
/*
* HTTP — minimal HTTP/1.1 server library (Phase 1)
*
* Goals for Phase 1:
* - HTTP/1.1 request parsing (request-line, headers)
* - Message body handling via Content-Length and Transfer-Encoding: chunked
* - Persistent connections (keep-alive) with Connection: close handling
* - Simple handler callback API: handler(req) -> res
* - No TLS, no pipelining, no compression, no auth (to be added later)
*
* API overview:
* class HTTP(number port)
* fun start(this, handler)
* - handler: function taking `req` map and returning `res` map
*
* Request map `req` fields:
* - method: string
* - target: raw request-target (string)
* - path: path part without query (string)
* - query: query string without '?' (string)
* - version: "HTTP/1.1" or "HTTP/1.0"
* - headers: map<string,string> (lower-cased header names)
* - body: string (for Phase 1; streaming may arrive later)
* - remote_fd: number (client socket fd)
*
* Response map `res` fields:
* - status: number (e.g. 200)
* - reason: optional string (defaults from common codes)
* - headers: optional map<string,string>
* - body: optional string (defaults to empty)
*/
// Utility: common status text
fun _http_reason(status)
code = to_number(status)
if (code == 200) return "OK"
if (code == 201) return "Created"
if (code == 202) return "Accepted"
if (code == 204) return "No Content"
if (code == 301) return "Moved Permanently"
if (code == 302) return "Found"
if (code == 303) return "See Other"
if (code == 304) return "Not Modified"
if (code == 307) return "Temporary Redirect"
if (code == 308) return "Permanent Redirect"
if (code == 400) return "Bad Request"
if (code == 401) return "Unauthorized"
if (code == 403) return "Forbidden"
if (code == 404) return "Not Found"
if (code == 405) return "Method Not Allowed"
if (code == 408) return "Request Timeout"
if (code == 411) return "Length Required"
if (code == 413) return "Payload Too Large"
if (code == 414) return "URI Too Long"
if (code == 415) return "Unsupported Media Type"
if (code == 417) return "Expectation Failed"
if (code == 426) return "Upgrade Required"
if (code == 431) return "Request Header Fields Too Large"
if (code == 500) return "Internal Server Error"
if (code == 501) return "Not Implemented"
if (code == 503) return "Service Unavailable"
if (code == 505) return "HTTP Version Not Supported"
return ""
// Utility: write full response and return whether connection should stay open
fun _http_write_response(fd, res, keep_alive)
status = to_number(res["status"])
reason = res["reason"]
if (len(to_string(reason)) == 0)
reason = _http_reason(status)
if (len(reason) == 0)
reason = "OK"
body = res["body"]
if (typeof(body) != "string")
body = to_string(body)
blen = len(body)
headers = res["headers"]
if (typeof(headers) != "map")
headers = {}
// Ensure Content-Length for simple bodies (no streaming in Phase 1)
headers["content-length"] = to_string(blen)
if (len(to_string(headers["content-type"])) == 0)
headers["content-type"] = "text/plain; charset=utf-8"
// Connection header per keep-alive flag
if (keep_alive)
headers["connection"] = "keep-alive"
else
headers["connection"] = "close"
// Build response string
start = "HTTP/1.1 " + to_string(status) + " " + reason + "\r\n"
head = ""
// Normalize: send headers with canonical casing best-effort
// but here we just send lower-case names as kept
for (k in keys(headers))
v = to_string(headers[k])
// join: "name: value\r\n"
head = head + k + ": " + v + "\r\n"
resp = start + head + "\r\n" + body
sock_send(fd, resp)
return keep_alive
// Utility: decode chunked transfer-encoding body from a buffer and fd (if needs more)
fun _http_read_chunked(fd, after_headers, max_body)
// after_headers: initial buffer containing any bytes after CRLFCRLF
data = after_headers
body = ""
// Read line by line: <hex-size> [;ext] CRLF, then that many bytes, then CRLF, repeat, then trailer part ends with CRLF CRLF
// For simplicity, we ignore chunk extensions and trailers in Phase 1.
while true
// ensure we have a full line
while find(data, "\r\n") < 0
more = sock_recv(fd, 4096)
if (len(more) == 0) return {"ok": 0, "body": body}
data = data + more
if (max_body > 0 && len(body) + len(data) > max_body) return {"ok": 0, "body": body}
pos = find(data, "\r\n")
line = substr(data, 0, pos)
data = substr(data, pos + 2, len(data) - pos - 2)
semi = find(line, ";")
size_str = line
if (semi >= 0)
size_str = substr(line, 0, semi)
size_str = str_trim(size_str)
// hex to number
// Fun doesn’t have hex parse; do manual
n = 0
i = 0
while i < len(size_str)
ch = substr(size_str, i, 1)
cl = str_to_lower(ch)
d = -1
if (cl >= "0" && cl <= "9") d = to_number(cl) - to_number("0")
else if (cl >= "a" && cl <= "f") d = 10 + (to_number(cl) - to_number("a"))
if (d < 0)
return {"ok": 0, "body": body}
n = n * 16 + d
i = i + 1
// read exactly n bytes
while len(data) < n + 2
more2 = sock_recv(fd, 4096)
if (len(more2) == 0) return {"ok": 0, "body": body}
data = data + more2
if (max_body > 0 && len(body) + len(data) > max_body) return {"ok": 0, "body": body}
chunk = substr(data, 0, n)
crlf = substr(data, n, 2)
// Must be CRLF after chunk
if (crlf != "\r\n") return {"ok": 0, "body": body}
data = substr(data, n + 2, len(data) - n - 2)
body = body + chunk
if (max_body > 0 && len(body) > max_body) return {"ok": 0, "body": body}
if (n == 0)
// reach end; ignore trailers (until CRLFCRLF)
// optional: consume trailers
// try to find CRLFCRLF in remaining data; if not there, read minimal amount
// but for Phase 1 we stop here
return {"ok": 1, "body": body}
class HTTP(number port)
// Configuration limits (conservative defaults)
max_header_bytes = 32 * 1024
max_start_line_bytes = 8 * 1024
max_body_bytes = 8 * 1024 * 1024 // 8 MiB for Phase 1
read_chunk_size = 4096
fun _construct(this, port)
this.port = port
this.server = TcpServer(port, 128)
// Start the HTTP server. `handler` is a function: handler(req) -> res
fun start(this, handler)
if (this.server.listen() <= 0)
print("HTTP: failed to listen on port " + to_string(this.port))
return 0
print("HTTP: listening on port " + to_string(this.port))
while true
cfd = this.server.accept()
if (cfd > 0)
this._handle_connection(cfd, handler)
return 1
// Connection loop with keep-alive
fun _handle_connection(this, fd, handler)
// read-buffer that may carry over data between requests on same socket
carry = ""
while true
parsed = this._read_and_parse_request(fd, carry)
if (parsed["ok"] != 1)
break
req = parsed["req"]
carry = parsed["carry"]
// handler returns res map
res = handler(req)
if (typeof(res) != "map")
res = {"status": 500, "reason": "Internal Server Error", "body": "Handler error"}
// decide keep-alive
h_conn = req["headers"]["connection"]
ver = req["version"]
// default keep-alive for HTTP/1.1 unless Connection: close
keep = (ver == "HTTP/1.1") && (str_to_lower(to_string(h_conn)) != "close")
if (str_to_lower(to_string(h_conn)) == "keep-alive")
keep = true
if (str_to_lower(to_string(h_conn)) == "close")
keep = false
stayed = _http_write_response(fd, res, keep)
if (!stayed)
break
sock_close(fd)
return 1
// Reads from fd until a full request is available or error; returns {ok, req, carry}
fun _read_and_parse_request(this, fd, carry)
data = carry
// Accumulate until we see end of headers (CRLFCRLF or LFLF)
// also enforce size limits
while true
// Check if headers end exists
p = find(data, "\r\n\r\n")
if (p < 0)
p = find(data, "\n\n")
if (p >= 0)
break
if (len(data) > this.max_header_bytes + this.max_start_line_bytes)
return {"ok": 0}
more = sock_recv(fd, this.read_chunk_size)
if (len(more) == 0)
return {"ok": 0}
data = data + more
// Split start-line and headers
// Find first line break
lf = find(data, "\n")
if (lf < 0 || lf > this.max_start_line_bytes)
return {"ok": 0}
// Handle CRLF or LF
first_line = substr(data, 0, (lf > 0 && substr(data, lf-1, 1) == "\r") ? lf-1 : lf)
headers_block = substr(data, (substr(data, lf-1, 1) == "\r") ? lf+1 : lf+1, // start after first line CRLF/LF
0) // placeholder, will correct below
// Find header-body separator
sep = find(data, "\r\n\r\n")
sep_len = 4
if (sep < 0)
sep = find(data, "\n\n")
sep_len = 2
headers_raw = substr(data, (substr(data, lf-1, 1) == "\r") ? lf+1 : lf+1, sep - ((substr(data, lf-1, 1) == "\r") ? (lf+1) : (lf+1)))
after = substr(data, sep + sep_len, len(data) - sep - sep_len)
// Parse start-line: METHOD SP request-target SP HTTP/version
parts = str_split(first_line, " ")
if (len(parts) < 3)
return {"ok": 0}
method = str_trim(parts[0])
target = str_trim(parts[1])
version = str_trim(parts[2])
if (version != "HTTP/1.1" && version != "HTTP/1.0")
return {"ok": 0}
// Parse headers into map with lower-cased names; ignore obs-fold
hdr_lines = str_split(headers_raw, "\n")
headers = {}
i = 0
while i < len(hdr_lines)
line = hdr_lines[i]
line = str_trim(line)
if (len(line) == 0)
i = i + 1
continue
colon = find(line, ":")
if (colon <= 0)
// invalid header; reject
return {"ok": 0}
name = str_to_lower(str_trim(substr(line, 0, colon)))
value = str_trim(substr(line, colon + 1, len(line) - colon - 1))
// merge duplicates with comma
prev = headers[name]
if (len(to_string(prev)) > 0)
headers[name] = prev + ", " + value
else
headers[name] = value
i = i + 1
// Handle request-target: split path and query
path = target
query = ""
qpos = find(target, "?")
if (qpos >= 0)
path = substr(target, 0, qpos)
query = substr(target, qpos + 1, len(target) - qpos - 1)
// Determine body
body = ""
// transfer-encoding has precedence
te = str_to_lower(to_string(headers["transfer-encoding"]))
cl = to_string(headers["content-length"])
if (len(te) > 0)
if (te == "chunked")
dec = _http_read_chunked(fd, after, this.max_body_bytes)
if (dec["ok"] != 1)
return {"ok": 0}
body = dec["body"]
after = ""
else
// unsupported TE
return {"ok": 0}
else if (len(cl) > 0)
// parse numeric Content-Length
n = 0
j = 0
while j < len(cl)
ch = substr(cl, j, 1)
if (!(ch >= "0" && ch <= "9"))
return {"ok": 0}
n = n * 10 + (to_number(ch) - to_number("0"))
j = j + 1
if (n > this.max_body_bytes)
return {"ok": 0}
// ensure we have n bytes in `after`, otherwise read
while len(after) < n
moreb = sock_recv(fd, this.read_chunk_size)
if (len(moreb) == 0)
return {"ok": 0}
after = after + moreb
if (len(after) > this.max_body_bytes)
return {"ok": 0}
body = substr(after, 0, n)
after = substr(after, n, len(after) - n)
else
// no body; leave `after` as carry for potential next requests
body = ""
req = {
"method": method,
"target": target,
"path": path,
"query": query,
"version": version,
"headers": headers,
"body": body,
"remote_fd": fd
}
return {"ok": 1, "req": req, "carry": after}

View file

@ -33,8 +33,8 @@ mastodon: https://fosstodon.org/@fun
truncatewords_length: 100000
# Set to your git root and branch to show a link to the file source in the footer. Unset to disable.
git: https://git.xw3.org/fun/fun/
git_short: https://git.xw3.org
git: https://xw3.org/fun/fun/
git_short: https://xw3.org
branch: src/branch/main/
lifetime: 3600

View file

@ -18,7 +18,7 @@
<li style="display:inline;">&nbsp;<a href="{{ site.mastodon }}" style="text-decoration:none;" title="Mastodon" class="mastodon" rel="me">Mastodon</a>&nbsp;-</li>
<li style="display:inline;">&nbsp;<a href="{{ site.git }}wiki" style="text-decoration:none;" title="Wiki" class="wiki">Wiki</a></li>
</ul>
<p style="text-align:center;"><a href="/copyright/" style="text-decoration:none;" title="Copyright">Copyright</a> &copy; {{ site.time | date: "%Y" }} - Johannes Findeisen (<a href="https://git.xw3.org/hanez" class="user">hanez</a>)</p>
<p style="text-align:center;"><a href="/copyright/" style="text-decoration:none;" title="Copyright">Copyright</a> &copy; {{ site.time | date: "%Y" }} - Johannes Findeisen (<a href="https://xw3.org/hanez" class="user">hanez</a>)</p>
</div>
</div>

View file

@ -15,4 +15,15 @@
<script src="/js/jquery-3.6.4.min.js" charset="{{ site.charset }}"></script>
<script src="/js/js-cookie-3.0.5.min.js" charset="{{ site.charset }}"></script>
<script src="/js/matomo.min.js" charset="{{ site.charset }}"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
const links = document.querySelectorAll('a');
links.forEach(link => {
if (link.hostname !== window.location.hostname) {
link.setAttribute('target', '_blank');
link.setAttribute('rel', 'noopener noreferrer');
}
});
});
</script>
</head>

View file

@ -18,7 +18,7 @@ layout:
</div>
<p style="text-align: center; color: #e8e8e8; font-size: 12px;">
Page generated at: {{ site.time | date: "%b %-d, %Y" }} using <a href="https://jekyllrb.com/">Jekyll</a> and <a href="https://git.xw3.org/xw3/xw3">xw3</a> on {{ site.os }}<br>
Page generated at: {{ site.time | date: "%b %-d, %Y" }} using <a href="https://jekyllrb.com/">Jekyll</a> and <a href="https://xw3.org/xw3/xw3">xw3</a> on {{ site.os }}<br>
<a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" style="text-decoration:none;" title="License"><img src="/images/by-nc-sa-88x31.png"></a></p>
<script src="/js/sorttable-2.min.js" charset="utf-8"></script>

View file

@ -3,7 +3,7 @@ layout: post
published: true
author: hanez
author_email: you@hanez.org
author_url: https://hanez.org
author_url: https://xw3.org/hanez
title: Init Fun!
description: Blog - Init Fun!
date: 2025-10-05

View file

@ -3,7 +3,7 @@ layout: post
published: true
author: hanez
author_email: you@hanez.org
author_url: https://hanez.org
author_url: https://xw3.org/hanez
noToc: false
title: Announcing Fun 0.37.62
description: Homepage and Blog
@ -20,7 +20,7 @@ tags:
- language
---
### First at all! Special thanks go to my wonderful girlfriend Sabine, my friends [Armin](https://m2m.pm/){:class="ext"} and [Ole](https://schizzoteam.org/){:class="ext"}, who motivated me over the past month to implement this. Cheers! Markus, I miss YOU, R.I.P!
### First at all! Special thanks go to my wonderful friends Sabine, [Armin](https://m2m.pm/){:class="ext"} and Ole, who motivated me over the past month to implement this. Cheers! Markus, I miss YOU, R.I.P!
We're excited to announce the release of Fun version 0.37.62, the latest iteration of our experimental programming language that prioritizes simplicity, consistency, and most importantly — joy in coding.
@ -142,12 +142,12 @@ Important: Fun is not developed on third-party Git servers like GitHub or GitLab
If you're curious about Fun, check out:
- [Git Repository](https://git.xw3.org/fun/fun){:class="git"}
- [Git Repository](https://xw3.org/fun/fun){:class="git"}
- [Fun Handbook](https://fun-lang.xyz/documentation/handbook/)
- [Fun REPL Guide](https://fun-lang.xyz/documentation/repl/)
- [Specification v0.4](https://git.xw3.org/fun/fun/src/branch/main/spec/v0.4.md){:class="git"}
- [Examples](https://git.xw3.org/fun/fun/src/branch/main/examples){:class="git"}
- [Standard Library](https://git.xw3.org/fun/fun/src/branch/main/lib){:class="git"}
- [Specification v0.4](https://xw3.org/fun/fun/src/branch/main/spec/v0.4.md){:class="git"}
- [Examples](https://xw3.org/fun/fun/src/branch/main/examples){:class="git"}
- [Standard Library](https://xw3.org/fun/fun/src/branch/main/lib){:class="git"}
- [Community](https://fun-lang.xyz/community/)
The examples directory contains demonstrations of most Fun features, from basic "Hello, World!" to threading, networking, classes, and more. The lib directory includes modules written in Fun itself - and in the future, most enhancements will be written in Fun rather than C.
@ -169,3 +169,4 @@ Fun is and will always be 100% free under the terms of the [Apache-2.0 License](
Happy hacking, and remember: Fun is Fun!
Johannes Findeisen (hanez) - [hanez@fun-lang.xyz](mailto:hanez@fun-lang.xyz){:class="mail"}

View file

@ -3,7 +3,7 @@ layout: post
published: true
author: hanez
author_email: you@hanez.org
author_url: https://hanez.org
author_url: https://xw3.org/hanez
noToc: false
title: Moving the Website to the Project Root
description: Relocating the fun-lang.xyz source code for easier maintenance.
@ -22,7 +22,7 @@ We've recently completed a significant change to our project structure: the sour
### Why the Move?
The main reason for merging this code into the root of the Fun project is that it is much easier for me to maintain the whole project this way.
The main reason for merging this code into the root of the Fun project is that it is much easier for me to maintain the whole project this way.
Having everything in one place simplifies our development workflow:
@ -43,3 +43,4 @@ For most users, nothing changes! The website remains available at [https://fun-l
Happy hacking!
Johannes Findeisen (hanez) - [hanez@fun-lang.xyz](mailto:hanez@fun-lang.xyz){:class="mail"}

View file

@ -3,12 +3,12 @@ layout: post
published: true
author: hanez
author_email: you@hanez.org
author_url: https://hanez.org
author_url: https://xw3.org/hanez
noToc: false
title: Announcing Fun 0.40.5
description: Nested functions, Rust integration, and CI automation
date: 2026-04-17
date_updated: 2026-04-18 00:17:13
date_updated: 2026-04-18
categories:
- news
- project
@ -85,8 +85,9 @@ The Fun VM is now more flexible than ever. Key parameters like `MAX_FRAMES`, `ST
As always, Fun remains an experiment, but with version 0.40.5, we are closer than ever to a stable 1.0. We've focused on cleaning up experimental features that didn't fit our minimalism philosophy while adding the powerful primitives needed for real-world hacking.
Want to try it out? Head over to our [Git Repository](https://git.xw3.org/fun/fun){:class="git"} or check out the updated [Documentation](https://fun-lang.xyz/documentation/).
Want to try it out? Head over to our [Git Repository](https://xw3.org/fun/fun){:class="git"} or check out the updated [Documentation](https://fun-lang.xyz/documentation/).
Happy hacking, and remember: Fun is Fun!
Johannes Findeisen (hanez) - [hanez@fun-lang.xyz](mailto:hanez@fun-lang.xyz){:class="mail"}

View file

@ -3,7 +3,7 @@ layout: post
published: true
author: hanez
author_email: you@hanez.org
author_url: https://hanez.org
author_url: https://xw3.org/hanez
noToc: false
title: Call for Participants and Sponsors
description: Join us in shaping the future of the Fun programming language
@ -46,7 +46,7 @@ If you or your organization are interested in supporting Fun, please reach out t
If you're interested in participating or sponsoring, please:
- Visit our [Git Repository](https://git.xw3.org/fun/fun){:class="git"} and check out the open issues.
- Visit our [Git Repository](https://xw3.org/fun/fun){:class="git"} and check out the open issues.
- Join our [community discussions](https://fun-lang.xyz/community/).
- Contact Johannes Findeisen (hanez) directly via [email](mailto:hanez@fun-lang.xyz){:class="mail"}.
@ -55,3 +55,4 @@ Every contribution, no matter how small, helps us move closer to our goal of mak
Happy hacking!
Johannes Findeisen (hanez) - [hanez@fun-lang.xyz](mailto:hanez@fun-lang.xyz){:class="mail"}

View file

@ -3,7 +3,7 @@ layout: post
published: true
author: hanez
author_email: hanez@fun-lang.xyz
author_url: https://hanez.org
author_url: https://xw3.org/hanez
noToc: false
title: Announcing the Fun API Documentation
description: A new API documentation hub is now available at fun-lang.xyz/api to explore the Fun VM internals and get contributors started faster.
@ -46,7 +46,7 @@ This is actually a work in progress, and we are continuously improving and expan
### How to get involved
- Browse the API docs at [https://api.fun-lang.xyz/](https://api.fun-lang.xyz/){:class="ext"} and send feedback.
- Open issues and ideas in our Git repository: [https://git.xw3.org/fun/fun](https://git.xw3.org/fun/fun){:class="git"}.
- Open issues and ideas in our Git repository: [https://xw3.org/fun/fun](https://xw3.org/fun/fun){:class="git"}.
- Join the discussion: [https://fun-lang.xyz/community/](https://fun-lang.xyz/community/).
- Reach out directly: [hanez@fun-lang.xyz](mailto:hanez@fun-lang.xyz){:class="mail"}.
@ -55,3 +55,4 @@ This is just the beginning - we will keep iterating on structure, coverage, and
Happy hacking!
Johannes Findeisen (hanez) - [hanez@fun-lang.xyz](mailto:hanez@fun-lang.xyz){:class="mail"}

View file

@ -3,7 +3,7 @@ layout: post
published: true
author: hanez
author_email: hanez@fun-lang.xyz
author_url: https://hanez.org
author_url: https://xw3.org/hanez
noToc: false
title: What is Fun?
description: Fun is a serious, focused programming language and VM aimed at professional environments — used for serial and smartcard communication and, potentially, TCP/Unix sockets.
@ -72,4 +72,5 @@ If you work with serial devices, smartcards, or lightweight networked tools and
Happy hacking!
Johannes Findeisen (hanez) - hanez@fun-lang.xyz
Johannes Findeisen (hanez) - [hanez@fun-lang.xyz](mailto:hanez@fun-lang.xyz){:class="mail"}

View file

@ -3,7 +3,7 @@ layout: post
published: true
author: hanez
author_email: you@hanez.org
author_url: https://hanez.org
author_url: https://xw3.org/hanez
noToc: false
title: Announcing Fun 0.42.1
description: Maintenance release with documentation updates and a small test script fix
@ -55,4 +55,5 @@ Thanks to everyone testing, filing issues, and contributing examples and docs. I
Happy hacking!
Johannes Findeisen (hanez) — hanez@fun-lang.xyz
Johannes Findeisen (hanez) - [hanez@fun-lang.xyz](mailto:hanez@fun-lang.xyz){:class="mail"}

View file

@ -93,62 +93,6 @@ blockquote {
}
}
pre {
background: $grey-color-dark;
/*background-image: -webkit-linear-gradient($grey-color-dark 50%, $grey-color-light 50%);
background-image: -moz-linear-gradient($grey-color-dark 50%, $grey-color-light 50%);
background-image: -ms-linear-gradient($grey-color-dark 50%, $grey-color-light 50%);
background-image: -o-linear-gradient($grey-color-dark 50%, $grey-color-light 50%);
background-image: linear-gradient($grey-color-dark 50%, $grey-color-light 50%);*/
background-position: 0 0;
background-repeat: repeat;
background-size: 2.8em 2.8em;
border: 0px $grey-color solid;
border-radius: 8px;
counter-reset: line-numbering;
font-family: $base-font-family;
font-size: $base-font-size;
// https://stackoverflow.com/questions/5840328/styling-each-line-inside-pre-with-css
line-height: 1.4em;
margin: 0 0 0 0;
overflow: auto;
overflow-x: auto;
padding: 8px;
tab-size: 2;
-moz-tab-size: 2;
}
/*pre::before {
content: counter(linex);
counter-increment: linex;
padding-right: 1em;
width: 1.5em;
text-align: right;
opacity: 0.5;
}*/
/*pre > code {
font-family: $base-font-family;
font-size: $base-font-size;
counter-increment: line;
margin: 0 0 0 0;
padding: 0 0 0 0;
}*/
/**pre > code:before{
content: counter(line);
display: inline-block;
width: 2.0em;
border-right: 1px solid $grey-color;
padding-right: .7em;
margin-right: .7em;
color: #bc6c21;
-webkit-user-select: none;
text-align: right;
}**/
audio {
height: 24px;
width: 100%;

26
web/_sass/_code.scss Normal file
View file

@ -0,0 +1,26 @@
code {
//-moz-tab-size: 2;
//tab-size: 2;
//border-radius: 12px;
font-family: "CourierPrimeSans";
font-size: $base-font-size;
//margin: 0 0 0 0;
//padding: 0 0 0 0;
//border: 1px $grey-color solid;
//line-height: 1.2em;
//background: $grey-color-dark;
//background-image: -webkit-linear-gradient($grey-color-dark 50%, $grey-color-light 50%);
//background-image: -moz-linear-gradient($grey-color-dark 50%, $grey-color-light 50%);
//background-image: -ms-linear-gradient($grey-color-dark 50%, $grey-color-light 50%);
//background-image: -o-linear-gradient($grey-color-dark 50%, $grey-color-light 50%);
//background-image: linear-gradient($grey-color-dark 50%, $grey-color-light 50%);
//background-position: 0 0;
//background-repeat: repeat;
//background-size: 2.8em 2.8em;
//white-space: pre-wrap;
//white-space: -moz-pre-wrap;
//white-space: -o-pre-wrap;
//word-wrap: break-word;
//width: 100%;
}

View file

@ -7,3 +7,38 @@
font-family: "MDI";
src: url("../fonts/materialdesignicons-webfont.woff2") format("woff2");
}
@font-face {
font-family: 'OpenSans-Regular';
src: url('/fonts/OpenSans-Regular.woff') format('woff');
}
@font-face {
font-family: 'MDI';
src: url('/fonts/materialdesignicons-webfont.woff2') format('woff2');
}
.mdi:before,
.mdi-set {
display: inline-block;
font: normal normal normal 24px/1 "MDI";
font-size: inherit;
text-rendering: auto;
line-height: inherit;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.mdi-hamburger::before {
content: "\F0685";
}
@font-face {
font-family: 'Graph-35-pix';
src: url('/fonts/Graph-35-pix.ttf');
}
@font-face {
font-family: 'CourierPrimeSans';
src: url('/fonts/Courier-Prime-Sans.ttf');
}

45
web/_sass/_pre.scss Normal file
View file

@ -0,0 +1,45 @@
pre {
//counter-reset: line;
-moz-tab-size: 2;
tab-size: 2;
border-radius: 12px;
//color: #363c3e;
font-family: "CourierPrimeSans";
//font-weight: bold;
font-size: $base-font-size;
margin: 0 0 0 0;
padding: 0 0 0 0.5em;
border: 1px $grey-color solid;
//overflow: auto;
overflow-x:auto;
line-height: 1.2em;
// https://stackoverflow.com/questions/5840328/styling-each-line-inside-pre-with-css
background: $grey-color-dark;
background-image: -webkit-linear-gradient($grey-color-dark 50%, $grey-color-light 50%);
background-image: -moz-linear-gradient($grey-color-dark 50%, $grey-color-light 50%);
background-image: -ms-linear-gradient($grey-color-dark 50%, $grey-color-light 50%);
background-image: -o-linear-gradient($grey-color-dark 50%, $grey-color-light 50%);
background-image: linear-gradient($grey-color-dark 50%, $grey-color-light 50%);
background-position: 0 0;
background-repeat: repeat;
background-size: 2.4em 2.4em;
white-space: pre;
}
//pre > code {
// margin: 0 0 0 0;
// padding: 0 0 0 0;
//}
/*pre.highlight > code:before{
content: counter(line);
display: inline-block;
width: 2.0em;
border-right: 1px solid $grey-color;
padding-right: .7em;
margin-right: .7em;
color: #bc6c21;
-webkit-user-select: none;
text-align: right;
}*/

View file

@ -31,4 +31,4 @@ $on-laptop: 1024px;
@content;
}
}
@import "base", "fonts", "forms", "highlight", "isso", "layout", "links", "toc";
@import "base", "code", "fonts", "forms", "highlight", "isso", "layout", "links", "pre", "toc";

View file

@ -20,7 +20,7 @@ tags:
---
This document summarizes the Fun bytecode format. For deep VM details, see [internals](./internals/).
This document summarizes the Fun bytecode format. For deep VM details, see [internals](/documentation/internals/).
## Goals
- Compact representation for fast loading and dispatch

View file

@ -24,12 +24,12 @@ This file serves as an index of the documents in this directory. Links are relat
- [REPL](./repl/) - REPL user guide: how to build/launch, editing and history, completions, REPL-on-error, and tips.
- [API](https://api.fun-lang.xyz/){:class="ext"} - API documentation for the Fun language and VM.
- Specification
- [v0.4](https://git.xw3.org/fun/fun/src/branch/main/spec/v0.4/){:class="git"}
- [v0.3](https://git.xw3.org/fun/fun/src/branch/main/spec/v0.3/){:class="git"}
- [v0.2](https://git.xw3.org/fun/fun/src/branch/main/spec/v0.2/){:class="git"}
- [v0.1](https://git.xw3.org/fun/fun/src/branch/main/spec/v0.1/){:class="git"}
- [Examples](https://git.xw3.org/fun/fun/src/branch/main/examples){:class="git"}
- [Standard Library](https://git.xw3.org/fun/fun/src/branch/main/lib){:class="git"}
- [v0.4](https://xw3.org/fun/fun/src/branch/main/spec/v0.4.md){:class="git"}
- [v0.3](https://xw3.org/fun/fun/src/branch/main/spec/v0.3.md){:class="git"}
- [v0.2](https://xw3.org/fun/fun/src/branch/main/spec/v0.2.md){:class="git"}
- [v0.1](https://xw3.org/fun/fun/src/branch/main/spec/v0.1.md){:class="git"}
- [Examples](https://xw3.org/fun/fun/src/branch/main/examples){:class="git"}
- [Standard Library](https://xw3.org/fun/fun/src/branch/main/lib){:class="git"}
The examples directory contains demonstrations of most Fun features, from basic "Hello, World!" to threading, networking, classes, and more. The lib directory includes modules written in Fun itself.
@ -86,3 +86,4 @@ Documentation for optional, build-time selectable integrations lives in [./exten
- For a broader project overview and quickstart, see the repository root [README](./../README/).
- Crypto examples:
- If built with `-DFUN_WITH_OPENSSL=ON`, try `examples/crypto/openssl_md5.fun`.

View file

@ -96,3 +96,4 @@ The website can be deployed using the Makefile:
make release</pre>
The `release` target builds the site, minifies the assets, and syncs the files to [fun-lang.xyz](https://fun-lang.xyz) via `rsync`. Ensure you have the necessary SSH permissions for the sync to succeed.

Binary file not shown.

BIN
web/fonts/Graph-35-pix.ttf Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
web/images/supportme.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View file

@ -19,6 +19,8 @@ permalink: /
<h2>What is Fun?</h2>
<p>This is open-source software maintained by volunteers. Contributions are welcome. However, personal attacks, memes, and expressions of blanket outrage in issue threads are unwelcome. Please be respectful.</p>
<p>Fun is a small, strict, and simple programming language that runs on a compact stack-based virtual machine. The C core is intentionally minimal; most functionality and standard libraries are implemented in Fun itself.</p>
<p>Fun is dynamically typed with optional static type annotations, <a href="/features/">featuring first-class functions</a>, classes with inheritance, pattern matching, and a rich standard library. It supports everything from basic scripting to TCP sockets, serial communication, threading, cryptography (MD5, SHA-1/256/384/512, CRC-32, AES-256), and a built-in debugger.</p>
@ -33,7 +35,7 @@ permalink: /
<!--<p><a href="/about/">More about Fun</a><p>-->
<h2>Blog (<a href="/feed/" title="Blog Feed" class="rss">Feed</a>)</h2>
<h2>News (<a href="/feed/" title="Blog Feed" class="rss">Feed</a>)</h2>
{% for post in site.tags.pinned %}
<article class="post-content">
@ -63,3 +65,4 @@ permalink: /
{% if count == paginator.page %}<a href="/{{ count }}/" class="active" title="Page {{ count }}">{{ count }}</a>{% else %}<a href="/{{ count }}/" title="Page {{ count }}">{{ count }}</a>{% endif %}
{% endfor %}
</div><br>