1
0
Fork 0
forked from fun/fun

Added user input support and many fixes. (0.16.5)

This commit is contained in:
Johannes Findeisen 2025-10-02 00:53:51 +02:00
commit 37abd5d6d3
11 changed files with 216 additions and 15 deletions

View file

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.16)
project(fun VERSION 0.16.4 LANGUAGES C)
project(fun VERSION 0.16.5 LANGUAGES C)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)

View file

@ -6,22 +6,22 @@ Fun is a highly strict programming language, but also highly simple. It looks li
Influenced by Bash, **[C](https://en.wikipedia.org/wiki/The_C_Programming_Language)**, Go, **[Lua](https://www.lua.org/)**, **[Python](https://www.python.org/)**, and Rust (Most influences came from linked languages).
Fun is and will ever be 100% free under the terms of the [ISC-License](https://opensource.org/license/isc-license-txt).
Fun is and will ever be 100% free under the terms of the [Apache-2.0-License](https://opensource.org/license/apache-2-0).
## Idea
* Simplicity
* Consistency
* Joy in coding
* Fun!
- Simplicity
- Consistency
- Joy in coding
- Fun!
## Characteristics
* Dynamic and optionally statically typed
* Type safety
* Written in C
* Internal libs are written with no_camel_case even when written in Fun
* Only a minimal function set is written in C and most other core libraries are implemented in Fun
- Dynamic and optionally statically typed
- Type safety
- Written in C and Fun
- Internal libs are written with no_camel_case even when written in Fun, except class Names
- Only a minimal function set is written in C, and most other core functions and libraries are implemented in Fun
## The Fun Manifesto
@ -65,7 +65,7 @@ Fun may not change the world — but it will make programming a little more fun.
## Documentation
Actually there does not exist any documenation. In the [examples/](https://git.xw3.org/fun/fun/src/branch/main/examples){:class="git"} directory should be an example of most Fun features.
Actually, there does not exist any documentation. In the [examples/](https://git.xw3.org/fun/fun/src/branch/main/examples) directory should be an example of most Fun features.
## Development
@ -103,7 +103,7 @@ This requires Cygwin to be installed and configured. I will not cover this here.
- Every commit message must contain the version at the end in the following format (1.2.3)
- Version numbering follows "[Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html)"
### Developement systems
### Development systems
- [GNU](https://gnu.org/)/[Linux](https://kernel.org/) ([Arch](https://archlinux.org/)/[Artix](https://artixlinux.org/), [Debian](https://www.debian.org/)) using [GCC](https://gcc.gnu.org/) and the [GNU C library](https://www.gnu.org/software/libc/) ([glibc](https://en.wikipedia.org/wiki/Glibc))
- GNU/Linux ([Alpine](https://alpinelinux.org/)) using GCC and the [musl libc](https://musl.libc.org/)

View file

@ -14,7 +14,7 @@
// Demonstrates local include:
// This resolves relative to the current working directory where 'fun' is executed.
#include "include_local_util.fun"
#include "examples/include_local_util.fun"
print("== include local demo ==")
greet("Fun")

47
examples/input_example.fun Executable file
View file

@ -0,0 +1,47 @@
#!/usr/bin/env fun
/*
* This file is part of the Fun programming language.
* https://hanez.org/project/fun/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
* Licensed under the terms of the Apache-2.0 license.
* https://opensource.org/license/apache-2-0
*
* Added: 2025-10-02
*/
/*
* Interactive input example using input() and Console wrapper.
*/
#include <io/console.fun>
print("=== input() builtin ===")
name = input("Enter your name: ")
print(join(["Hello, ", name, "!"], ""))
print("=== Console class ===")
c = Console()
city = c.ask("Your city")
print(join(["Nice to meet you from ", city], ""))
yn = c.ask_yes_no("Do you like Fun?")
if yn
print("Great! 🎉")
else
print("Give it a try, it grows on you.")
/* Input given:
Enter your name: Hanez
Your city: Universe
Do you like Fun? [y/n]: y
*/
/* Expected output:
=== input() builtin ===
Hello, Hanez!
=== Console class ===
Nice to meet you from Universe
Great! 🎉
*/

View file

@ -52,3 +52,39 @@ bytes = [0x48,0x65,0x6c,0x6c,0x6f] // "Hello"
b64 = b64_encode_bytes(bytes)
print(b64) // "SGVsbG8="
print(join(b64_decode_to_bytes(b64), ",")) // "72,101,108,108,111"
/* Expected output:
=== Arrays ===
2,2,3
4,3,2,2,1
3
0
1,2,3,4
1,2,3,4,5
=== Strings ===
[Hello World]
1
1
a|b|c
baNANA
fun
FUN
hahaha
=== Math ===
5
10
6
42
243
2
9
1
3
=== Range ===
0,1,2,3,4
3,4,5,6,7
10,7,4,1
=== Base64 ===
SGVsbG8=
72,101,108,108,111
*/

31
lib/io/console.fun Normal file
View file

@ -0,0 +1,31 @@
/*
* Console utilities: prompt, ask, and yes/no helpers built on input().
*/
#include <strings.fun>
class Console()
// Print a prompt and read a line (no trailing newline)
fun prompt(this, text)
return input(to_string(text))
// Ask a question with ": " suffix; returns the user's response string
fun ask(this, question)
q = to_string(question)
if (len(q) == 0)
return input("")
else
return input(join([q, ": "], ""))
// Ask a yes/no question; returns 1 for yes, 0 for no
// Accepts: y, yes, n, no (case-insensitive). Keeps asking until valid.
fun ask_yes_no(this, question)
q = to_string(question)
while true
ans = input(join([q, " [y/n]: "], ""))
a = str_to_lower(ans)
if (a == "y" || a == "yes")
return 1
else if (a == "n" || a == "no")
return 0
// otherwise loop again

View file

@ -114,6 +114,7 @@ typedef enum {
// OS
OP_ENV, // pops name string; pushes value string (or "")
OP_INPUT_LINE, // operand: 0=no prompt; 1=has prompt. Pops [prompt?]; pushes input string (no trailing newline)
// Threads
OP_THREAD_SPAWN, // operand: 0=no args, 1=has args; pops [args?], fn; pushes thread id (int>0)

View file

@ -641,6 +641,19 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
free(name);
return 1;
}
if (strcmp(name, "input") == 0) {
(*pos)++; /* '(' */
int hasPrompt = 0;
skip_spaces(src, len, pos);
if (*pos < len && src[*pos] != ')') {
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "input expects 0 or 1 argument"); free(name); return 0; }
hasPrompt = 1;
}
if (!consume_char(src, len, pos, ')')) { parser_fail(*pos, "Expected ')' after input arg(s)"); free(name); return 0; }
bytecode_add_instruction(bc, OP_INPUT_LINE, hasPrompt ? 1 : 0);
free(name);
return 1;
}
if (strcmp(name, "env") == 0) {
(*pos)++; /* '(' */
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "env expects 1 argument"); free(name); return 0; }

View file

@ -289,6 +289,7 @@ void vm_run(VM *vm, Bytecode *entry) {
#include "vm/io/read_file.c"
#include "vm/io/write_file.c"
#include "vm/io/input_line.c"
#include "vm/logic/and.c"
#include "vm/logic/eq.c"

View file

@ -33,7 +33,7 @@ static const char *opcode_names[] = {
"ENUMERATE","ZIP",
"MIN","MAX","CLAMP","ABS","POW","RANDOM_SEED","RANDOM_INT",
"MAKE_MAP","KEYS","VALUES","HAS_KEY",
"READ_FILE","WRITE_FILE","ENV",
"READ_FILE","WRITE_FILE","ENV","INPUT_LINE",
"THREAD_SPAWN","THREAD_JOIN","SLEEP_MS",
"BAND","BOR","BXOR","BNOT","SHL","SHR","ROTL","ROTR"
};

72
src/vm/io/input_line.c Normal file
View file

@ -0,0 +1,72 @@
case OP_INPUT_LINE: {
/* operand: 0 = no prompt; 1 = has prompt (string or any value convertible to string) */
int has_prompt = inst.operand ? 1 : 0;
if (has_prompt) {
/* pop prompt value and print without newline */
Value pv = pop_value(vm);
char *pstr = value_to_string_alloc(&pv);
if (pstr) {
fputs(pstr, stdout);
fflush(stdout);
free(pstr);
}
free_value(pv);
}
/* read a line from stdin, dynamically grow buffer */
size_t cap = 128;
size_t len = 0;
char *buf = (char*)malloc(cap);
if (!buf) {
fprintf(stderr, "Runtime error: out of memory reading input");
push_value(vm, make_string(""));
break;
}
int ch;
while ((ch = fgetc(stdin)) != EOF) {
if (ch == '\r') {
/* Handle CRLF by consuming optional following '\n' */
int next = fgetc(stdin);
if (next != EOF && next != '\n') {
ungetc(next, stdin);
}
break;
}
if (ch == '\n') {
break;
}
if (len + 1 >= cap) {
cap *= 2;
char *nb = (char*)realloc(buf, cap);
if (!nb) {
free(buf);
fprintf(stderr, "Runtime error: out of memory reading input");
push_value(vm, make_string(""));
goto push_done;
}
buf = nb;
}
buf[len++] = (char)ch;
}
/* null-terminate */
if (len + 1 >= cap) {
char *nb = (char*)realloc(buf, len + 1);
if (!nb) {
free(buf);
fprintf(stderr, "Runtime error: out of memory finalizing input");
push_value(vm, make_string(""));
goto push_done;
}
buf = nb;
}
buf[len] = '\0';
/* push as Fun string */
push_value(vm, make_string(buf));
free(buf);
push_done:
break;
}