16 Commits

Author SHA1 Message Date
Arthur Barraux eae85c32ca add utf8_char_t struct 2025-11-19 10:37:41 +01:00
arthur c06c820dfb Patch path complete and read char lisp function
Build project / build (push) Has been cancelled
2025-11-07 16:23:56 +01:00
arthur 5588b0a8d7 add path autocomplete
Build project / build (push) Has been cancelled
2025-11-05 15:49:01 +01:00
arthur 419e924650 Add search function
Build project / build (push) Has been cancelled
2025-11-03 16:45:23 +01:00
arthur 6a201b3ebc delete line macro
Build project / build (push) Has been cancelled
2025-11-03 16:05:25 +01:00
arthur 1d253e51ef Update README.md
Build project / build (push) Failing after 3m27s
2025-11-01 13:22:10 +01:00
Arthur Barraux 42f82e2e0d add packages
Build project / build (push) Has been cancelled
2025-11-01 13:03:07 +01:00
arthur fa32f4b177 Update src/init.c
Build project / build (push) Has been cancelled
2025-10-31 09:10:57 +01:00
arthur 65f997e964 Update install.sh
Build project / build (push) Has been cancelled
2025-10-31 09:09:20 +01:00
Arthur Barraux 7eaf6913cb Merge remote-tracking branch 'gitea/main'
Build project / build (push) Has been cancelled
2025-10-30 18:18:12 +01:00
Arthur Barraux 8f7dcf3534 Adding installer script 2025-10-30 18:17:19 +01:00
arthur d8c6b9ace3 Update README.md
Build project / build (push) Has been cancelled
2025-10-28 09:16:36 +01:00
arthur d0173d7308 Update README.md
Build project / build (push) Has been cancelled
2025-10-28 09:02:29 +01:00
arthur 40fc234eeb Update .gitea/workflows/build.yml
Build project / build (push) Failing after 39s
2025-10-18 15:35:57 +02:00
Arthur Barraux 756deba83e remove .cache repo for cleaning
Build project / build (push) Failing after 33s
2025-10-18 15:12:06 +02:00
Arthur Barraux 85e8067e41 clean up the repo
Build project / build (push) Failing after 1m22s
2025-10-18 15:06:23 +02:00
78 changed files with 931 additions and 25600 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ on:
jobs: jobs:
build: build:
runs-on: home-1 runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout code
+17 -10
View File
@@ -1,26 +1,33 @@
# Beluga # Beluga
Beluga is a project of CLI text editor that will fit perfectly with your azerty keyboard. Beluga is a project of CLI text editor that uses lisp as configuration language.
It's abviously only working for **Linux**.
## Requirements ## Requirements
You will only need **cmake** and **clang** to compile the editor. You will only need **meson** and a **C compiler** to compile the editor.
## Installation ## Installation
### From source
Here is the installation line for development version: Here is the installation line for development version:
```git clone --recurse-submodules https://github.com/le-cocotier/beluga.git ~/.beluga && cd ~/.beluga && mkdir build && cd build && cmake ../ && make beluga``` ```git clone https://homelinuxserver.ddns.net/git/arthur/beluga.git ~/.beluga && cd ~/.beluga && meson setup build && meson compile -C build```
The executable file will be in `bin/beluga`. Feel free to add it to your path. The executable file will be `build/beluga`. Feel free to add it to your path.
You can either run `make all` or `make doc_doxygen` if you're interested by the doxygen documentation. ### From installation script ( prefered )
Just clone the repo and execute the script `install.sh`. It will automatically add beluga to your path.
## Getting start ## Getting start
To open an existing file just type : To open an existing file just type :
```beluga path_to_my_file``` ```./build/beluga path_to_my_file```
The only keybinds that you will need will be : Here is some few command that you will need :
- Ctrl-Q : leave the editor
- Ctrl-S : Save a file | keybind| command |
|--------|------------------|
| Ctrl-Q | leave the editor |
| Ctrl-S | Save a file |
| Ctrl-O | open file |
+3 -3
View File
@@ -16,7 +16,7 @@
;; KEY MAPPING ;; KEY MAPPING
(map-key "CTRL-q" editor-quit) (map-key "CTRL-q" editor-quit)
(map-key "CTRL-s" editor-save) (map-key "CTRL-d" editor-save)
(map-key "ARROW-UP" '(move-cursor "up")) (map-key "ARROW-UP" '(move-cursor "up"))
(map-key "ARROW-DOWN" '(move-cursor "down")) (map-key "ARROW-DOWN" '(move-cursor "down"))
(map-key "ARROW-RIGHT" '(move-cursor "right")) (map-key "ARROW-RIGHT" '(move-cursor "right"))
@@ -29,6 +29,6 @@
(map-key "PAGE-UP" move-cursor-page-up) (map-key "PAGE-UP" move-cursor-page-up)
(map-key "PAGE-DOWN" move-cursor-page-down) (map-key "PAGE-DOWN" move-cursor-page-down)
(map-key "CTRL-o" editor-open-file) (map-key "CTRL-o" editor-open-file)
(map-key "CTRL-k" editor-del-row)
(map-key "CTRL-s" editor-find)
+1 -1
View File
@@ -5,7 +5,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
void abAppend(struct abuf *ab, const char *s, int len); void abAppend(struct abuf *ab, const unsigned char *s, int len);
void abFree(struct abuf *ab); void abFree(struct abuf *ab);
+9 -1
View File
@@ -1,7 +1,7 @@
#ifndef BUILTINS_H_ #ifndef BUILTINS_H_
#define BUILTINS_H_ #define BUILTINS_H_
#include "../lisp-interpreter/dist/lisp.h" #include "lisp.h"
Lisp moveCursor(Lisp args, LispError *e, LispContext ctx); Lisp moveCursor(Lisp args, LispError *e, LispContext ctx);
@@ -29,4 +29,12 @@ Lisp editorOpenFile(Lisp args, LispError *e, LispContext ctx);
Lisp editorPrintC(Lisp args, LispError *e, LispContext ctx); Lisp editorPrintC(Lisp args, LispError *e, LispContext ctx);
Lisp addPackage(Lisp args, LispError *e, LispContext ctx);
Lisp editorDelRow_L(Lisp args, LispError *e, LispContext ctx);
Lisp editorFind_L(Lisp args, LispError *e, LispContext ctx);
Lisp editorReadChar_L(Lisp args, LispError *e, LispContext ctx);
#endif #endif
+48 -7
View File
@@ -5,7 +5,13 @@
#include <termios.h> #include <termios.h>
#include <time.h> #include <time.h>
#include "../lisp-interpreter/dist/lisp.h" #include "lisp.h"
typedef struct{
unsigned char c[4];
char len;
} utf_8_char_t;
/** /**
* \struct erow * \struct erow
@@ -16,8 +22,8 @@
typedef struct erow { typedef struct erow {
int size; /**< Size of the line */ int size; /**< Size of the line */
int rsize; /**< Size of the render line */ int rsize; /**< Size of the render line */
char *chars; /**< Characters of the line */ utf_8_char_t *chars; /**< Characters of the line */
char *render; /**< The actual line we will print */ utf_8_char_t *render; /**< The actual line we will print */
} erow; } erow;
enum editorStatus_e { enum editorStatus_e {
@@ -31,10 +37,45 @@ struct const_t {
int QUIT_TIMES; int QUIT_TIMES;
}; };
struct keyBind_t { // Key types
char *key_sequence; typedef enum {
Lisp command; KEY_CHAR, // Regular character or UTF-8
KEY_CTRL, // Ctrl+letter
KEY_ALT, // Alt+letter
KEY_ARROW, // Arrow keys
KEY_FUNCTION, // Function keys
KEY_SPECIAL, // Tab, Enter, ESC, Backspace, etc.
KEY_NAVIGATION, // Home, End, PgUp, PgDn, Insert, Delete
KEY_UNKNOWN
} KeyType;
// Modifiers
typedef enum {
MOD_NONE = 0,
MOD_SHIFT = 1,
MOD_ALT = 2,
MOD_CTRL = 4
} KeyModifier;
// Key information structure
typedef struct {
KeyType type;
int modifiers; // Bitmask of KeyModifier
union {
unsigned int codepoint; // For KEY_CHAR
char ctrl_char; // For KEY_CTRL (A-Z)
char alt_char; // For KEY_ALT
char arrow; // For KEY_ARROW (U/D/L/R)
int function_num; // For KEY_FUNCTION (1-12)
char special; // For KEY_SPECIAL and KEY_NAVIGATION
} data;
utf_8_char_t c; // Raw bytes
} KeyInfo;
struct keyBind_t {
KeyInfo *key_sequence;
Lisp command;
}; };
/** /**
@@ -77,7 +118,7 @@ struct editorConfig {
* */ * */
struct abuf { struct abuf {
char *b; /**< Text that will be printed */ unsigned char *b; /**< Text that will be printed */
int len; /**< Length of the text */ int len; /**< Length of the text */
}; };
+3 -12
View File
@@ -8,19 +8,10 @@
#define HIDE_CURSOR "\x1b[?25l" #define HIDE_CURSOR "\x1b[?25l"
#define SHOW_CURSOR "\x1b[?25h" #define SHOW_CURSOR "\x1b[?25h"
#define ERASE_END_LINE "\x1b[K" #define ERASE_END_LINE "\x1b[K"
#define TAB "\x09"
#define SPACE "\x20"
enum editorKey {
BACKSPACE = 127,
ARROW_LEFT = 1000,
ARROW_RIGHT,
ARROW_UP,
ARROW_DOWN,
DEL_KEY,
BEG_LINE,
END_LINE,
PAGE_UP,
PAGE_DOWN,
};
#define ABUF_INIT {NULL, 0} #define ABUF_INIT {NULL, 0}
+2 -2
View File
@@ -2,9 +2,9 @@
#define EDITOR_OP_H_ #define EDITOR_OP_H_
#include "data.h" #include "data.h"
void editorInsertChar(int c); void editorInsertChar(utf_8_char_t *c);
void editorInsertNewLine(); void editorInsertNewLine(void);
void editorDelChar(); void editorDelChar();
+2
View File
@@ -17,4 +17,6 @@ void editorOpen(char *filename);
void editorSave(); void editorSave();
void editorFind();
#endif // FILE_IO_H_ #endif // FILE_IO_H_
+3 -3
View File
@@ -20,13 +20,13 @@
// END \x1b[4~ || <esc>[8~ || <esc>[F || <esc>OF // END \x1b[4~ || <esc>[8~ || <esc>[F || <esc>OF
// DELETE \x1b[3~ // DELETE \x1b[3~
char *editorPrompt(char *prompt); char *editorPrompt(char *prompt, char * PlaceHolder, char bPathMode);
char *key_to_string(int key); char *key_to_string(int key);
void editorMoveCursor(int key); void editorMoveCursor(KeyInfo * key);
int executeKeyBind(char *key_sequence); int executeKeyBind(KeyInfo *key_sequence);
/** /**
* \fn void editorProcessKeypress() * \fn void editorProcessKeypress()
+3 -1
View File
@@ -10,6 +10,8 @@
int editorRowCxToRx(erow *row, int cursor_x); int editorRowCxToRx(erow *row, int cursor_x);
int editorRowRxToCx(erow *row, int rx);
void editorUpdateRow(erow *row); void editorUpdateRow(erow *row);
void editorInsertRow(int at, char *s, size_t len); void editorInsertRow(int at, char *s, size_t len);
@@ -18,7 +20,7 @@ void editorFreeRow(erow *row);
void editorDelRow(int at); void editorDelRow(int at);
void editorRowInsertChar(erow *row, int at, int c); void editorRowInsertChar(erow *row, int at, utf_8_char_t c);
void editorRowAppendString(erow *row, char *s, size_t len); void editorRowAppendString(erow *row, char *s, size_t len);
+3 -1
View File
@@ -25,10 +25,12 @@ void disableRawMode();
void enableRawMode(); void enableRawMode();
int editorReadKey(); KeyInfo * editorReadKey();
int getCursorPosition(int *rows, int *cols); int getCursorPosition(int *rows, int *cols);
KeyInfo *stringToCodepoint(const char *string);
int getWindowSize(int *rows, int *cols); int getWindowSize(int *rows, int *cols);
#endif #endif
Executable
+43
View File
@@ -0,0 +1,43 @@
#!/bin/bash
echo "--- Welcome to Beluga installer ---"
read -p "Do you want to start the installation ? (Y/n)" confirm && [[ $confirm == [yY] || $confirm == [yY][eE][sS] ]] || exit 1
# Check dependencies
if ! command -v "meson" &>/dev/null; then
echo "❌ Error: meson not found. Please install it first."
exit 1
fi
# Create config files
echo "Create config files ..."
mkdir -pv ~/.beluga/
cp -rv ./assets/ ~/.beluga/
mkdir -pv ~/.beluga/config/
mkdir -pv ~/.beluga/packages/
read -p "Do you want to replace your config file or keep it (init.lisp.bak) / (init.lisp.new) ? (Y/n)" confirm
if [[ "$confirm" =~ ^[Yy]$ ]]; then
mv ~/.beluga/config/init.lisp ~/.beluga/config/init.lisp.bak
cp -rv ./config/init.lisp ~/.beluga/config/
else
cp -rv ./config/init.lisp ~/.beluga/config/init.lisp.new
fi
# Compile the project
echo "Start compilation ..."
meson setup build/
meson compile -C build/
# Add to path
echo "Adding beluga to the path"
sudo cp -f ./build/beluga /usr/local/bin/
echo "Installation finish"
echo "Check ~/.beluga/config/init.lisp for customization"
-27
View File
@@ -1,27 +0,0 @@
*.out
/lisp
*.exe
*.stackdump
temp/
/printer
/sample
# Xcode
.DS_Store
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
*.xcworkspace
!default.xcworkspace
xcuserdata
profile
*.moved-aside
DerivedData
.idea/
-64
View File
@@ -1,64 +0,0 @@
# Internals
Technical design decisions and tricks.
## Memory
We do not use tagged pointers, for simplicity and portability.
`Lisp` objects are fairly large due to alignment requirements (16 bytes).
However, they are usually only stored in this form when interacting
in the C stack. In data structures, we prefer to store `LispVal` (8 bytes)
and pack the types in with the block info.
All allocations are aligned to `sizeof(LispVal)` to avoid unaligned access.
- [Chicken representation](http://www.more-magic.net/posts/internals-data-representation.html)
## Garbage Collection
The choice to use explicit, rather than automatic garbage collection, was made so that the interpreter does not need to keep track of every lisp object on the stack, only the most important objects.
If garbage collection was allowed to trigger at any time in the middle of a C function call, then the interpreter would need to be able to "see" all the lisp values on the call stack, in order to prevent them from being collected. Providing this feature would make integrating with C code much more complicated and conflict with the project's goal of being easily embeddable.
This means that when `lisp_collect` is called, all lisp values which are not reachable from the global environment or the function's parameters become invalidated. Be conscious of when and where you call the garbage collector.
An alternative solution is used in [Lua][lua-memory].
The interpreter uses the [Cheney algorithim][cheney-mta] for garbage collection. Memory is allocated in fixed size pages. When an allocation is request and the current page does not have enough space remaining, a new page will be allocated to fulfill the allocation. So, allocations will continue to use up more memory until garbage collection.
Note that tail call recursion will not overflow the stack, but will use additional memory for each function call.
[cheney-mta]: https://en.wikipedia.org/wiki/Cheney%27s_algorithm
[mta-info]: http://home.pipeline.com/~hbaker1/CheneyMTA.html
[lua-memory]: https://www.lua.org/pil/24.2.html
[gc-internals]: http://www.more-magic.net/posts/internals-gc.html
## General design
- [Lysp][lysp]
- [Lispy][lispy]
[lysp]: http://piumarta.com/software/lysp/
[lispy]: http://norvig.com/lispy.html
## Environments/Tables
Tables are only resized at garbage collect time.
This leads us to linked list-chaining as opposed to open addresing
because linked list chaining can scale infinitely while
waiting for garbage collection.
- [SICP][sicp-environments]
- [MIT][environment-objects]
[sicp-environments]: https://mitpress.mit.edu/sicp/full-text/book/book-Z-H-21.html#%_sec_3.2
[environment-objects]: https://groups.csail.mit.edu/mac/ftpdir/scheme-7.4/doc-html/scheme_14.html
What is the cost of a helper (nested) function?
It must allocate a new lambda, but it doesn't have to read/expand it again.
## Symbols
- Reference counting symbol table? - http://sandbox.mc.edu/~bennet/cs404/ex/lisprcnt.html
-26
View File
@@ -1,26 +0,0 @@
CFLAGS = -Idist/ -Wall -pedantic -Wstrict-prototypes -O3
LDLIBS = -lm
CC=cc
all: lisp printer sample
clean:
rm -f lisp
rm -f printer
rm -f sample
rm -f dist/lisp_lib.h
lisp: repl.c dist/lisp.h dist/lisp_lib.h
${CC} repl.c -o $@ ${CFLAGS} ${LDLIBS}
printer: printer.c dist/lisp.h
${CC} printer.c -o $@ ${CFLAGS} ${LDLIBS}
sample: sample.c dist/lisp.h dist/lisp_lib.h
${CC} sample.c -o $@ ${CFLAGS} ${LDLIBS}
dist/lisp_lib.h: stdlib/lib.h stdlib/lib.c
cd stdlib; ./concat.sh > ../$@;
.PHONY: all clean
-207
View File
@@ -1,207 +0,0 @@
Lisp Interpreter
================
> Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp. -- Philip Greenspun
An embeddable lisp/scheme interpreter written in C.
It includes a subset of R5RS with some extensions from MIT Scheme.
I created this while reading [SICP](https://github.com/justinmeiners/sicp-excercises) to improve my knowledge of lisp
and to make an implementation that allows me to easily add scripting to my own programs.
### Philosophy
- **Simple**: This project doesn't aim to be optimal, or fully standards compliant.
It is just a robust foundation for scripting.
It is implemented as a recursive AST walker on the C stack.
If you need more try [s7](https://ccrma.stanford.edu/software/snd/snd/s7.html) or [chicken](https://www.call-cc.org)
- **Unintrusive**: Just copy in the header file. Turn on and off major features with build macros. It should be portable between major platforms.
- **Unsurprising**: You should be able to read the source code and understand how it works.
The C API should work how you expect.
- **First class data**: Lisp s-expressions are undervalued as an alternative to JSON or XML.
Preprocessor flags can remove most scheme features if you just want to read s-expressions
and manipulate them in C.
### Features
- C99, no dependencies, two files.
- Core lisp language `if`, `let`, `do`, `lambda`, `cons`, `eval`, etc.
- Subset of scheme R5RS library: lists, vectors, hash tables, integers, real numbers, characters, strings, and integers.
- Common lisp goodies: unhygenic macros (`define-macro`), `push`, `dotimes`.
- Easy to integrate C functions.
- Exact [garbage collection](#garbage-collection) with explicit invocation.
- REPL command line tool.
- Efficient parsing and manipulation of large data files.
### Non-Features
- Compiler or VM.
- Full numeric tower.
- Full call/cc. This only supports simple stack jumps.
- syntax rules.
- UNIX system interface/IO library.
### Examples
### Interactive programming with Read, eval, print loop.
```bash
$ ./lisp
> (define (sqr x) (* x x)))
> (define length 40)
> (define area 0)
> (set! area (sqr length))
1600
```
### Quickstart
```c
LispContext ctx = lisp_init();
lisp_load_lib(ctx);
LispError error;
Lisp program = lisp_read("(+ 1 2)", &error, ctx);
Lisp result = lisp_eval(program, &error, ctx);
if (error != LISP_ERROR_NONE)
lisp_print(result); ; => 3
lisp_shutdown(ctx);
```
### Loading Data
Lisp s-expressions can be used as a lightweight substitute to JSON or XML.
Looking up keys which are reused is even more efficient due to symbol comparison.
JSON
```json
{
"name" : "Bob Jones",
"age" : 54,
"city" : "SLC",
}
```
Lisp
```scheme
#((name . "Bob Jones")
(age . 54)
(city . "SLC"))
```
Loading the structure in C.
```c
LispContext ctx = lisp_init();
// load lisp structure
Lisp data = lisp_read_file(file, ctx);
// get value for age
Lisp age_entry = lisp_avector_ref(data, lisp_make_symbol("AGE", ctx), ctx);
// ...
lisp_shutdown(ctx);
```
### Calling C functions
C functions can be used to extend the interpreter, or call into C code.
```c
Lisp integer_range(Lisp args, LispError* e, LispContext ctx)
{
// first argument
LispInt start = lisp_int(lisp_car(args));
args = lisp_cdr(args);
// second argument
LispInt end = lisp_int(lisp_car(args));
if (end < start)
{
*e = LISP_ERROR_OUT_OF_BOUNDS;
return lisp_null();
}
LispInt n = end - start;
Lisp numbers = lisp_make_vector(n, ctx);
for (LispInt i = 0; i < n; ++i)
lisp_vector_set(numbers, i, lisp_make_int(start + i));
return numbers;
}
// ...
// wrap in Lisp object
Lisp func = lisp_make_func(integers_in_range);
// add to enviornment with symbol INTEGER-RANGE
Lisp env = lisp_env_global(ctx);
lisp_env_define(env, lisp_make_symbol("INTEGER-RANGE", ctx), func, ctx);
```
In Lisp
```scheme
(integer-range 5 15)
; => #(5 6 7 8 9 10 11 12 13 14)
```
Constants can also be stored in the environment in a similar fashion.
```c
Lisp pi = lisp_make_real(3.141592);
lisp_env_define(env, lisp_make_symbol("PI", ctx), pi, ctx);
```
### Macros
Common Lisp style (`defmacro`) is available with the name `define-macro`.
(define-macro nil! (lambda (x)
`(set! ,x '()))
### Garbage Collection
Garbage is only collected if it is explicitly told to.
You can invoke the garbage collector in C:
lisp_collect(ctx);
OR in lisp code:
(gc-flip)
Note that whenever a collect is issued
ANY `Lisp` value in `C`which is not accessible
through the global environment may become invalid.
Be careful what variables you hold onto in C.
Don't call `eval` in a custom defined C function unless you know what you are doing.
See [internals](INTERNALS.md) for more details.
## Documentation
For the language refer to [MIT Scheme](https://groups.csail.mit.edu/mac/ftpdir/scheme-7.4/doc-html/scheme_toc.html)
with the understanding that not everything is missing.
If we do implement a feature that MIT scheme has, we will try to follow their specificaiton.
For the C API refer to the header and sample programs (`repl.c`, `printer.c`).
## Project License
Copyright (c) 2020 Justin Meiners
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-24
View File
@@ -1,24 +0,0 @@
#include <stdlib.h>
#include <time.h>
#include <string.h>
#define LISP_IMPLEMENTATION
#include "lisp.h"
int main(int argc, const char* argv[])
{
LispContext ctx = lisp_init();
LispError error;
Lisp data = lisp_read_file(stdin, &error, ctx);
if (error != LISP_ERROR_NONE)
{
fprintf(stderr, "error: %s\n", lisp_error_string(error));
}
data = lisp_collect(data, ctx);
lisp_printf(stdout, data);
lisp_shutdown(ctx);
return 0;
}
-163
View File
@@ -1,163 +0,0 @@
#include <stdlib.h>
#include <time.h>
#include <string.h>
// Disable asserts?
// #define NDEBUG
//#define LISP_DEBUG
#define LISP_IMPLEMENTATION
#include "lisp.h"
#include "lisp_lib.h"
#define LINE_MAX 4096
static Lisp sch_load(Lisp args, LispError* e, LispContext ctx)
{
Lisp path = lisp_car(args);
Lisp result = lisp_read_path(lisp_string(path), e, ctx);
if (*e != LISP_ERROR_NONE) return lisp_null();
return lisp_eval(result, e, ctx);
}
int main(int argc, const char* argv[])
{
const char* file_path = NULL;
int run_script = 0;
int verbose;
#ifdef LISP_DEBUG
verbose = 1;
#else
verbose = 0;
#endif
for (int i = 1; i < argc; ++i)
{
if (strcmp(argv[i], "--load") == 0)
{
file_path = argv[i + 1];
}
if (strcmp(argv[i], "--script") == 0)
{
file_path = argv[i + 1];
run_script = 1;
}
}
//LispContext ctx = lisp_init();
LispContext ctx = lisp_init_with_lib();
lisp_env_define(
lisp_cdr(lisp_env(ctx)),
lisp_make_symbol("LOAD", ctx),
lisp_make_func(sch_load),
ctx
);
// Load as a macro is called "include" and can be used to load files containing macros.
lisp_table_set(
lisp_macro_table(ctx),
lisp_make_symbol("INCLUDE", ctx),
lisp_make_func(sch_load),
ctx
);
clock_t start_time, end_time;
if (file_path)
{
if (verbose)
{
printf("loading: %s\n", file_path);
}
start_time = clock();
LispError error;
Lisp l = lisp_read_path(file_path, &error, ctx);
if (error != LISP_ERROR_NONE)
{
fprintf(stderr, "%s. %s\n", file_path, lisp_error_string(error));
}
end_time = clock();
if (verbose)
printf("read (us): %lu\n", 1000000 * (end_time - start_time) / CLOCKS_PER_SEC);
start_time = clock();
Lisp code = lisp_macroexpand(l, &error, ctx);
if (error != LISP_ERROR_NONE)
{
fprintf(stderr, "%s\n", lisp_error_string(error));
exit(1);
}
end_time = clock();
if (verbose)
printf("expand (us): %lu\n", 1000000 * (end_time - start_time) / CLOCKS_PER_SEC);
start_time = clock();
lisp_eval(code, &error, ctx);
end_time = clock();
if (error != LISP_ERROR_NONE)
{
fprintf(stderr, "%s\n", lisp_error_string(error));
exit(1);
}
lisp_collect(lisp_null(), ctx);
if (verbose)
printf("eval (us): %lu\n", 1000000 * (end_time - start_time) / CLOCKS_PER_SEC);
}
if (!run_script)
{
// REPL
while (!feof(stdin))
{
printf("> ");
char line[LINE_MAX];
if (!fgets(line, LINE_MAX, stdin)) break;
clock_t start_time = clock();
LispError error;
Lisp code = lisp_read(line, &error, ctx);
if (error != LISP_ERROR_NONE)
{
fprintf(stderr, "%s\n", lisp_error_string(error));
continue;
}
Lisp l = lisp_eval(code, &error, ctx);
clock_t end_time = clock();
if (error != LISP_ERROR_NONE)
{
fprintf(stderr, "%s\n", lisp_error_string(error));
}
lisp_printf(stdout, l);
printf("\n");
lisp_collect(lisp_null(), ctx);
if (verbose)
printf("(us): %lu\n", 1000000 * (end_time - start_time) / CLOCKS_PER_SEC);
}
}
lisp_shutdown(ctx);
return 0;
}
-57
View File
@@ -1,57 +0,0 @@
#!/bin/sh
PASS=1
cd tests/code
for FILE in *.scm
do
echo "$FILE"
../../lisp --script "$FILE"
RESULT=$?
printf "\n"
if [ $RESULT = "0" ]
then
echo "FINISHED $FILE"
else
echo "*FAILED* $FILE"
PASS=0
fi
printf "\n"
done
cd ../
cd data
( ./test.sh )
RESULT=$?
if [ $RESULT = "0" ]
then
echo "FINISHED data test"
else
echo "*FAILED* data test"
PASS=0
fi
cd ../
cd printer
( ./test.sh )
RESULT=$?
if [ $RESULT = "0" ]
then
echo "FINISHED printer test"
else
echo "*FAILED* printer test"
PASS=0
fi
if [ $PASS = "0" ]
then
echo "**TESTS FAILED**"
else
echo "**TESTS PASSED**"
fi
-55
View File
@@ -1,55 +0,0 @@
#include <stdlib.h>
#include <time.h>
#include <string.h>
#define LISP_IMPLEMENTATION
#include "lisp.h"
#include "lisp_lib.h"
Lisp integer_range(Lisp args, LispError* e, LispContext ctx)
{
// first argument
LispInt start = lisp_int(lisp_car(args));
args = lisp_cdr(args);
// second argument
LispInt end = lisp_int(lisp_car(args));
if (end < start)
{
*e = LISP_ERROR_OUT_OF_BOUNDS;
return lisp_null();
}
LispInt n = end - start;
Lisp numbers = lisp_make_vector(n, ctx);
for (LispInt i = 0; i < n; ++i)
lisp_vector_set(numbers, i, lisp_make_int(start + i));
return numbers;
}
int main(int argc, const char* argv[])
{
LispContext ctx = lisp_init();
lisp_lib_load(ctx);
// wrap in Lisp object
Lisp func = lisp_make_func(integer_range);
// add to enviornment with symbol INTEGER-RANGE
Lisp env = lisp_env(ctx);
lisp_env_define(env, lisp_make_symbol("INTEGER-RANGE", ctx), func, ctx);
Lisp pi = lisp_make_real(3.141592);
lisp_env_define(env, lisp_make_symbol("PI", ctx), pi, ctx);
LispError e;
Lisp result = lisp_eval(lisp_read("(INTEGER-RANGE 5 15)", &e, ctx), &e, ctx);
lisp_printf(stdout, result);
if (e != LISP_ERROR_NONE) fprintf(stderr, "error: %s\n", lisp_error_string(e));
lisp_shutdown(ctx);
return 0;
}
-90
View File
@@ -1,90 +0,0 @@
(define-macro lambda (/\_ args
(if (pair? args)
(if (pair? (cdr args))
(if (pair? (cdr (cdr args)))
`(/\_ ,(car args) ,(cons 'BEGIN (cdr args)))
`(/\_ ,(car args) ,(car (cdr args))))
(syntax-error "lambda missing body expressions: (lambda (args) body)"))
(syntax-error "lambda missing argument: (lambda (args) body)"))))
(define-macro set! (lambda (var x)
(begin
(if (not (symbol? var)) (syntax-error "set! not a variable"))
`(_SET! ,var ,x))))
(define-macro define
(lambda (var . exprs)
(if (symbol? var)
(if (pair? (cdr exprs))
(syntax-error "define: (define var x)")
`(_DEF ,var ,(car exprs)))
(if (pair? var)
`(_DEF ,(car var)
(LAMBDA ,(cdr var)
,(if (null? (cdr exprs)) (car exprs) (cons 'BEGIN exprs))))
(syntax-error "define: not a symbol") ))))
(define (first x) (car x))
(define (second x) (car (cdr x)))
(define (third x) (car (cdr (cdr x))))
(define (some? pred l)
(if (null? l) #f
(if (pred (car l)) #t
(some? pred (cdr l)))))
(define (_map1-helper proc l result)
(if (null? l)
(reverse! result)
(_map1-helper proc
(cdr l)
(cons (proc (car l)) result))))
(define (map1 proc l) (_map1-helper proc l '()))
(define (for-each1 proc l)
(if (null? l) '()
(begin (proc (car l)) (for-each1 proc (cdr l )))))
(define (reverse! l) (append-reverse! l '()))
(define (reverse l) (reverse! (list-copy l)))
(define (last-pair x)
(if (pair? (cdr x))
(last-pair (cdr x)) x))
(define (list-tail x k)
(if (zero? k) x
(list-tail (cdr x) (- k 1))))
(define (fold-left op acc lst)
(if (null? lst) acc
(fold-left op (op acc (car lst)) (cdr lst))))
(define (_expand-shorthand-body path)
(if (null? path) (cons 'pair '())
(list (if (char=? (car path) #\A)
(cons 'CAR (_expand-shorthand-body (cdr path)))))))
(define (_expand-shorthand text)
(cons 'DEFINE (cons (list (string->symbol (string-append "C" text "R")) 'pair)
(_expand-shorthand-body (string->list text)))))
(define-macro _shorthand-accessors (lambda args (cons 'BEGIN (map1 _expand-shorthand args))))
(define (vector . args) (list->vector args))
(define (vector-copy v) (subvector v 0 (vector-length v)))
(define (vector-head v end) (subvector v 0 end))
(define (vector-tail v start) (subvector v start (vector-length v)))
(define (string . chars) (list->string chars))
(define (string>=? a b) (not (string<? a b)))
(define (string>? a b) (string<? b a))
(define (string<=? a b) (not (string<? b a)))
(define (string-copy s) (substring s 0 (string-length v)))
(define (string-head s end) (subvector s 0 end))
(define (string-tail s start) (subvector s start (string-length v)))
-78
View File
@@ -1,78 +0,0 @@
(define (_make-lambda args body)
(list 'LAMBDA args (if (null? (cdr body)) (car body) (cons 'BEGIN body))))
; (LET <name> ((<var0> <expr0>) ... (<varN> <expr1>)) <body0> ... <bodyN>)
; => ((LAMBDA (<var0> ... <varN>) (BEGIN <body0> ... <bodyN>)) <expr0> ... <expr1>)
; => named
; ((lambda ()
; (define <name> (LAMBDA (<var0> ... <varN>) (BEGIN <body0> ... <bodyN>)))
; (<name> <expr0> ... <exprN>)))
(define (_check-binding-list bindings)
(for-each1 (lambda (entry)
(if (not (pair? entry)) (syntax-error "bad let binding" entry))
(if (not (symbol? (first entry))) (syntax-error "let entry missing symbol" entry))) bindings))
(define (_let->combination var bindings body)
(_check-binding-list bindings)
(define body-func (_make-lambda (map1 (lambda (entry) (first entry)) bindings) body))
(define initial-args (map1 (lambda (entry) (second entry)) bindings))
(if (null? var)
(cons body-func initial-args)
(list (_make-lambda '() (list (list 'DEFINE var body-func) (cons var initial-args))))))
(define-macro let (lambda args
(if (pair? (first args))
(_let->combination '() (car args) (cdr args))
(_let->combination (first args) (second args) (cdr (cdr args))))))
(define (_let*-helper bindings body)
(if (null? bindings) (if (null? (cdr body)) (car body) (cons 'BEGIN body))
(list 'LET (list (car bindings)) (_let*-helper (cdr bindings) body))))
(define-macro let* (lambda (bindings . body)
(_check-binding-list bindings)
(_let*-helper bindings body)))
(define-macro letrec (lambda (bindings . body)
(_check-binding-list bindings)
(cons (_make-lambda (map1 (lambda (entry) (first entry)) bindings)
(append (map1 (lambda (entry) (list 'SET! (first entry) (second entry)))
bindings) body))
(map1 (lambda (entry) '()) bindings))))
; (COND (<pred0> <expr0>)
; (<pred1> <expr1>)
; ...
; (else <expr-1>))
; =>
; (IF <pred0> <expr0>
; (if <pred1> <expr1>
; ....
; (if <predN> <exprN> <expr-1>)) ... )
(define (_cond-check-clauses clauses)
(for-each1 (lambda (clause)
(if (not (pair? clause)) (syntax-error "cond: invalid clause"))
(if (null? (cdr clause)) (syntax-error "cond: clause missing expression")))
clauses))
(define (_cond-helper clauses)
(if (null? clauses) '()
(if (eq? (car (car clauses)) 'ELSE)
(cons 'BEGIN (cdr (car clauses)))
(list 'IF
(car (car clauses))
(cons 'BEGIN (cdr (car clauses)))
(_cond-helper (cdr clauses))))))
(define-macro cond (lambda clauses
(begin
(_cond-check-clauses clauses)
(_cond-helper clauses))))
-75
View File
@@ -1,75 +0,0 @@
(_shorthand-accessors "AA" "DD" "AD" "DA" "AAA" "AAD" "ADA" "DAA" "ADD" "DAD" "DDA" "DDD")
(define (_and-helper preds)
(cond ((null? preds) #t)
((null? (cdr preds)) (car preds))
(else
`(IF ,(car preds) ,(_and-helper (cdr preds)) #f))))
(define-macro and (lambda preds (_and-helper preds)))
(define (_or-helper preds var)
(cond ((null? preds) #f)
((null? (cdr preds)) (car preds))
(else
`(BEGIN (SET! ,var ,(car preds))
(IF ,var ,var ,(_or-helper (cdr preds) var))))))
(define-macro or (lambda preds
(let ((var (gensym)))
`(LET ((,var '())) ,(_or-helper preds var)))))
(define-macro case (lambda (key . clauses)
(let ((expr (gensym)))
`(LET ((,expr ,key))
,(cons 'COND (map1 (lambda (entry)
(cons (if (pair? (car entry))
`(MEMV ,expr (quote ,(car entry)))
(car entry))
(cdr entry))) clauses))))))
(define-macro push
(lambda (v l)
`(begin (set! ,l (cons ,v ,l)) ,l)))
; (DO ((<var0> <init0> <step0>) ...) (<test> <result>) <body>)
(define-macro do
(lambda (vars loop-check . loops)
(let ( (names '()) (inits '()) (steps '()) (f (gensym)) )
(for-each1 (lambda (var)
(push (car var) names)
(set! var (cdr var))
(push (car var) inits)
(set! var (cdr var))
(push (car var) steps)) vars)
`((LAMBDA ()
(DEFINE ,f (LAMBDA ,names
(IF ,(car loop-check)
,(if (pair? (cdr loop-check)) (car (cdr loop-check)) '())
,(cons 'BEGIN (append loops (list (cons f steps)))) )))
,(cons f inits)
)) )))
(define-macro dotimes
(lambda (form body)
(apply (lambda (i n . result)
`(DO ((,i 0 (+ ,i 1)))
((>= ,i ,n) ,(if (null? result) result (car result)) )
,body)
) form)))
(define-macro swap!
(lambda (x y)
(let ((temp (gensym)))
`(LET ((,temp ,x))
(SET! ,temp ,x)
(SET! ,x ,y)
(SET! ,y ,temp)))))
(define-macro inc! ; CL incf
(lambda (x)
`(SET! ,x (+ ,x 1))))
(define-macro dec! ; CL decf
(lambda (x)
`(SET! ,x (- ,x 1))))
-35
View File
@@ -1,35 +0,0 @@
(define (number? x) (real? x))
(define (odd? x) (not (even? x)))
(define (inexact? x) (not (exact? x)))
(define (zero? x) (= x 0))
(define (positive? x) (>= x 0))
(define (negative? x) (< x 0))
(define (>= a b) (not (< a b)))
(define (> a b) (< b a))
(define (<= a b) (not (< b a)))
(define (max . ls)
(fold-left (lambda (m x)
(if (> x m)
x
m)) (car ls) (cdr ls)))
(define (min . ls)
(fold-left (lambda (m x)
(if (< x m)
x
m)) (car ls) (cdr ls)))
(define (_gcd-helper a b)
(if (= b 0) a (_gcd-helper b (modulo a b))))
(define (gcd . args)
(if (null? args) 0
(_gcd-helper (car args) (car (cdr args)))))
(define (lcm . args)
(if (null? args) 1
(abs (* (/ (car args) (apply gcd args))
(apply * (cdr args))))))
-133
View File
@@ -1,133 +0,0 @@
(define (map proc . rest)
(define (helper lists result)
(if (some? null? lists)
(reverse! result)
(helper (map1 cdr lists)
(cons (apply proc (map1 car lists)) result))))
(helper rest '()))
(define (for-each proc . rest)
(define (helper lists)
(if (some? null? lists)
'()
(begin
(apply proc (map1 car lists))
(helper (map1 cdr lists)))))
(helper rest))
(define (filter pred l)
(define (helper l result)
(cond ((null? l) result)
((pred (car l))
(helper (cdr l) (cons (car l) result)))
(else
(helper (cdr l) result))))
(reverse! (helper l '())))
(define (reduce op default lst)
(if (null? lst)
default
(fold-left op (car lst) (cdr lst))))
(define (alist->hash-table alist)
(define h (make-hash-table))
(for-each1 (lambda (pair)
(hash-table-set! h (car pair) (cdr pair))) alist)
h)
(define (_assoc key list eq?)
(if (null? list) #f
(let ((pair (car list)))
(if (and (pair? pair) (eq? key (car pair)))
pair
(_assoc key (cdr list) eq?)))))
(define (assoc key list) (_assoc key list equal?))
(define (assq key list) (_assoc key list eq?))
(define (assv key list) (_assoc key list eqv?))
(define (_member x list eq?)
(cond ((null? list) #f)
((eq? (car list) x) list)
(else (_member x (cdr list) eq?))))
(define (member x list) (_member x list equal?))
(define (memq x list) (_member x list eq?))
(define (memv x list) (_member x list eqv?))
(define (make-initialized-vector l fn)
(let ((v (make-vector l '())))
(do ((i 0 (+ i 1)))
((>= i l) v)
(vector-set! v i (fn i)))))
(define (vector-map fn v)
(make-initialized-vector
(vector-length v)
(lambda (i) (fn (vector-ref v i)))))
(define (vector-binary-search v key< unwrap-key key)
(define (helper low high mid)
(if (<= (- high low) 1)
(if (key< (unwrap-key (vector-ref v low)) key) #f (vector-ref v low))
(begin
(set! mid (+ low (quotient (- high low) 2)))
(if (key< key (unwrap-key (vector-ref v mid)))
(helper low mid 0)
(helper mid high 0)))))
(helper 0 (vector-length v) 0))
(define (_insertsort v lo hi op)
(if (= (- hi lo) 0)
v
(do ((i (+ lo 1) (+ i 1)))
((= i hi) v)
(define x (vector-ref v i))
(do ((j i (- j 1)))
((or
(= j lo)
(not (op x (vector-ref v (- j 1)))))
(vector-set! v j x))
(vector-set! v j (vector-ref v (- j 1))))) ))
(define (_quicksort-partition v lo hi op)
(let ((pivot (vector-ref v (+ lo (/ (- hi lo) 2))))
(i (- lo 1))
(j (+ hi 1)))
(do ()
((>= i j) j)
(set! i (+ i 1))
(do ()
((not (op (vector-ref v i) pivot)) '())
(set! i (+ i 1)))
(set! j (- j 1))
(do ()
((not (op pivot (vector-ref v j))) '())
(set! j (- j 1)))
(if (< i j) (vector-swap! v i j))
)))
(define (_quicksort-vector v lo hi threshold op)
(if (and (>= lo 0) (>= hi 0) (< lo hi) (> (- hi lo) threshold))
(let ((p (_quicksort-partition v lo hi op)))
(_quicksort-vector v lo p threshold op)
(_quicksort-vector v (+ p 1) hi threshold op))))
(define (sort! v op)
; quicksort down to a certain level of recursion and
; then use insertion sort to finalize.
(_quicksort-vector v 0 (- (vector-length v) 1) 16 op)
(_insertsort v 0 (vector-length v) op)
v)
(define (sort list cmp) (vector->list (sort! (list->vector list) cmp)))
-58
View File
@@ -1,58 +0,0 @@
(define-macro delay (lambda (expr)
`(make-promise ,(cons 'LAMBDA
(cons '()
(cons expr '()))))))
(define (force promise)
(if (not (promise-forced? promise))
(_promise-store! promise ((_promise-procedure promise))))
(promise-value promise))
(define-macro cons-stream (lambda (x expr) `(cons ,x (delay ,expr))))
(define (stream-car stream) (car stream))
(define (stream-cdr stream) (force (cdr stream)))
(define (stream-pair? x)
(and (pair? x) (promise? (cdr x))))
(define (stream-null? stream) (null? stream))
(define (stream->list-helper stream result)
(if (stream-null? stream)
(reverse! result)
(stream->list-helper
(force (cdr stream))
(cons (car stream) result))))
(define (stream->list stream)
(stream->list-helper stream '()))
(define (list->stream list)
(if (null? list)
'()
(cons-stream (car list) (list->stream (cdr list)))))
(define (stream . args) (list->stream args))
(define (stream-head-helper stream k result)
(if (= k 0)
(reverse! result)
(stream-head-helper (force (cdr stream)) (- k 1) (cons (car stream) result))))
(define (stream-head stream k)
(stream-head-helper stream k '()))
(define (stream-tail stream k)
(if (= k 0)
stream
(stream-tail (stream-cdr stream) (- k 1))))
(define (stream-filter pred stream)
(cond ((stream-null? stream) the-empty-stream)
((pred (stream-car stream))
(cons-stream (stream-car stream)
(stream-filter pred
(stream-cdr stream))))
(else (stream-filter pred (stream-cdr stream)))))
-63
View File
@@ -1,63 +0,0 @@
(define (char>=? a b) (not (char<? a b)))
(define (char>? a b) (char<? b a))
(define (char<=? a b) (not (char<? b a)))
(define (char-ci=? a b) (char=? (char-downcase a) (char-downcase b)))
(define (char-ci<? a b) (char<? (char-downcase a) (char-downcase b)))
(define (char-ci>=? a b) (not (char-ci<? a b)))
(define (char-ci>? a b) (char-ci<? b a))
(define (char-ci<=? a b) (not (char-ci<? b a)))
(define (char-lower-case? c)
(and (>= (char->integer c) (char->integer #\a))
(<= (char->integer c) (char->integer #\z))))
(define (char-upper-case? c)
(and (>= (char->integer c) (char->integer #\A))
(<= (char->integer c) (char->integer #\Z))))
(define (procedure? p) (or (compiled-procedure? p) (compound-procedure? p)))
(define (current-input-port) _current-input-port)
(define (current-output-port) _current-output-port)
(define (read . args)
(_read (if (null? args)
(current-input-port)
(car args))))
(define (write obj . args)
(_write obj (if (null? args)
(current-output-port)
(car args))))
(define (display obj . args)
(_display obj (if (null? args)
(current-output-port)
(car args))))
(define (write-char obj . args)
(_write-char obj (if (null? args)
(current-output-port)
(car args))))
(define (flush-output-port . args)
(_flush-output-port (if (null? args)
(current-output-port)
(car args))))
(define (newline) (write-char #\newline))
(define-macro assert (lambda (body)
`(if ,body '()
(begin
(display (quote ,body))
(error " assert failed")))))
(define-macro ==> (lambda (test expected)
`(assert (equal? ,test (quote ,expected))) ))
-20
View File
@@ -1,20 +0,0 @@
#!/bin/bash
SRC=*.scm
cat lib.h
echo "// Generated from scheme source."
echo "#ifdef LISP_IMPLEMENTATION"
for FILE in $SRC
do
echo "static const char* lib_$(basename $FILE .scm)_src_ = "
cat $FILE | ./text2c.sh
echo ";"
echo ""
done
cat lib.c
echo "#endif"
File diff suppressed because it is too large Load Diff
-31
View File
@@ -1,31 +0,0 @@
/*
Created by: Justin Meiners
Repo; https://github.com/justinmeiners/lisp-interpreter
License: MIT (See end if file)
This file contains the scheme standard library.
See lisp.h for insructions.
*/
#ifndef LISP_LIB_H
#define LISP_LIB_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
#include <stdint.h>
void lisp_lib_load(LispContext ctx);
// convenience for init and load
LispContext lisp_init_with_lib(void);
#ifdef __cplusplus
}
#endif
#endif
-9
View File
@@ -1,9 +0,0 @@
#!/bin/bash
read -r -d '' content
content=${content//'\'/'\\'}
content=${content//$'\t'/'\t'}
content=${content//$'\n'/$' \\n\\\n'}
content=${content//$'\r'/'\r'}
content=${content//'"'/'\"'}
echo -n "\"$content\""
-78
View File
@@ -1,78 +0,0 @@
; a1 a2 ... an
; We must end up with n multiplications of three numbers.
; Each one must have a distinct ai in the middle.
; (* aj ai ak) where j < i and i < k.
; The neighboring elements are determined by the order of selection,
; so we can just look at the order of select elements. This is all the permutations
; of i in [1...n] so n! total possibilites.
; However, this also tells us how to estimate.
(defun smallest-index (array)
"requires: array is non-empty"
(prog ((smallest 0)
(i 0)
(N (length array)))
loop
(when (>= i N)
(return smallest))
(when (< (aref array i) (aref array smallest))
(setf smallest i))
(incf i)
(go loop)))
(defun ref (array i)
(if (or (< i 0) (>= i (length array)))
1
(aref array i)))
(defun estimate (array)
(if (= (length array) 1)
(aref array 0)
(let ((i (smallest-index array)))
(+
(*
(ref array i)
(ref array (- i 1))
(ref array (+ i 1)))
(estimate (concatenate 'vector
(subseq array 0 i)
(subseq array (+ i 1))))))))
(defun upper-bound (array)
(loop for i from 0 below (length array) sum
(* (reduce #'max (subseq array 0 i) :initial-value 1)
(aref array i)
(reduce #'max (subseq array (+ i 1)) :initial-value 1))))
(defun upper-bound2 (array)
(let ((N (length array))
(x (reduce #'max array)))
(+ (* (- N 2) (* x x x))
(* (* x x))
(* x))
))
(defun lower-bound2 (array)
(let ((N (length array))
(x (reduce #'min array)))
(+ (* (- N 2) (* x x x))
(* (* x x))
(* x))))
(print (estimate #(5 3 2 8)))
(print (estimate #(10 10 10 10 10 10)))
;(print (upper-bound #(5 3 2 8)))
;(print (lower-bound2 #(10 10 10 10 10 10)))
;(print (upper-bound2 #(10 10 10 10 10 10)))
@@ -1,34 +0,0 @@
; Find the largest palindrome made from the product of two 3-digit numbers.
; could be 6 digits
; 10^3 = 1,000 possible palindromes
; or 5 digits
; 10^3 = 1,000 possible palindromes
; 2,000 total
(define (palindrome? n)
(define (check i str)
(if (> (* i 2) (string-length str))
#t
(if (eq? (string-ref str i)
(string-ref str (- (string-length str) (+ i 1))))
(check (+ i 1) str)
#f)))
(check 0 (number->string n)))
(define nums '())
(do ((i 100 (+ i 1)))
((> i 999) 'done)
(do ((j 100 (+ j 1)))
((> j 999) 'done)
(let ((n (* i j)))
(if (>= n 100000)
(if (palindrome? n)
(push n nums)
)))))
(display (reduce max 0 nums))
-47
View File
@@ -1,47 +0,0 @@
(define large-num (string-append
"73167176531330624919225119674426574742355349194934"
"96983520312774506326239578318016984801869478851843"
"85861560789112949495459501737958331952853208805511"
"12540698747158523863050715693290963295227443043557"
"66896648950445244523161731856403098711121722383113"
"62229893423380308135336276614282806444486645238749"
"30358907296290491560440772390713810515859307960866"
"70172427121883998797908792274921901699720888093776"
"65727333001053367881220235421809751254540594752243"
"52584907711670556013604839586446706324415722155397"
"53697817977846174064955149290862569321978468622482"
"83972241375657056057490261407972968652414535100474"
"82166370484403199890008895243450658541227588666881"
"16427171479924442928230863465674813919123162824586"
"17866458359124566529476545682848912883142607690042"
"24219022671055626321111109370544217506941658960408"
"07198403850962455444362981230987879927244284909188"
"84580156166097919133875499200524063689912560717606"
"05886116467109405077541002256983155200055935729725"
"71636269561882670428252483600823257530420752963450"))
(define width 13)
(define (char->number c)
(if (char-numeric? c)
(- (char->integer c) (char->integer #\0))
-1))
(define (substring->numbers subs)
(map char->number (string->list subs)))
(define (iter i largest)
(if (>= (+ i width) (string-length large-num))
largest
(let* ((sub (substring large-num i (+ i width)))
(set (substring->numbers sub))
(product (apply * set)))
(if (> product (car largest))
(iter (+ i 1) (cons product set))
(iter (+ i 1) largest)))))
(display (iter 0 '(-1 . '())))
-56
View File
@@ -1,56 +0,0 @@
; add a test for every bug that is incountered
; to avoid recreating it in the future
; test basic vector creation and operations
(define v #(1 2 3 4 5 6 7 8 9 10))
(define (sum-to-n n) (/ (* n (+ n 1)) 2))
(define (sum-vector v)
(define (iter sum i)
(if (= i (vector-length v))
sum
(iter (+ sum (vector-ref v i)) (+ i 1))))
(iter 0 0))
(display "Sum to 10: ")
(display (sum-vector v))
(newline)
(assert (= (sum-to-n 10) (sum-vector v)))
; procedures with no arguments don't expand properly
(define (hello-world) (display "hello world") (newline))
(hello-world)
; vector and list assoc
(assert
(= (do ((i 1 (+ i 1)) (n 0 n))
((> i 10) n)
(set! n (+ i n)))
(* 5 11)))
(let ((sym (gensym)))
(assert (eq? sym sym)))
(assert (equal? (cons 2000 1) (cons 2000 1)))
(assert (equal? "apple" "apple"))
(assert (not (eq? 'DEFINE 'DEFINE-MACRO)))
(define (scope-test var)
(let ((var "dog"))
(==> var "dog"))
(==> var "cat"))
(scope-test "cat")
(define (scope-test-named var)
(let block-name ((var "dog"))
(==> var "dog"))
(==> var "cat"))
(scope-test-named "cat")
-59
View File
@@ -1,59 +0,0 @@
; PROBLEW
; W - maximum bag weight
; {m_1, m_2, ... m_n} = item weights
; {v_1, v_2, ... v_n} = item values
; want to choose a subset I so that
; sum m_i <= W
; and
; sum v_i is maximized
; In other words, if we choose another subset J
; then sum v_j <= sum v_i
(define (rand-item max-weight max-cost)
(cons (random max-weight)
(random max-cost)))
(define (build-items n)
(if (= n 0)
'()
(cons (rand-item 100 100)
(build-items (- n 1)))))
;(random-seed! (GET-UNIVERSAL-TIME))
;(define items (build-items 10))
(define items '((23 . 505) (26 . 352) (18 . 220) (32 . 354) (27 . 414) (29 . 498) (26 . 545) (30 . 473) (27 . 543)))
(define (knapsack remaining items)
(if (or (null? items) (<= remaining 0))
0
(let ((weight (car (car items)))
(val (cdr (car items))))
(max
(if (>= (- remaining weight) 0)
(+ val (knapsack (- remaining weight) (cdr items)))
0)
(knapsack remaining (cdr items))))))
(display (knapsack 67 items))
(assert (= (knapsack 67 items) 1270))
; https://en.wikipedia.org/wiki/Levenshtein_distance
(define (edit-distance-list a b eq?)
(cond ((null? a) (length b))
((null? b) (length a))
(else (min
(+ 1 (edit-distance-list (cdr a) b eq?)) ; insert
(+ 1 (edit-distance-list a (cdr b) eq?)) ; delete
(+
(if (eq? (car a) (car b)) 0 1) ; replace if needed
(edit-distance-list (cdr a) (cdr b) eq?))))))
(define (edit-distance a b)
(edit-distance-list (string->list a) (string->list b) char=?))
(==> (edit-distance "kitten" "sitting") 3)
@@ -1,5 +0,0 @@
(load "include/draw-tree.scm")
(dt '(a b c (d e f (g . h))))
(dt '((a) (b . c) (d e)))
-93
View File
@@ -1,93 +0,0 @@
; copy examples from MIT scheme documentation and add related ones.
; Conditionals
; https://www.gnu.org/software/mit-scheme/documentation/stable/mit-scheme-ref/Conditionals.html
(assert (and (= 2 2) (> 2 1)))
(assert (and))
(==> (and 3 2) 2)
(==> (and 1 2 'c '(f g)) (f g))
(==> (or #f #\a #f) #\a)
(==> (or (memq 'b '(a b c)) (/ 3 0)) (b c))
(define (bit-type x)
(cond ((= x 0) 'OFF)
((= x 1) 'ON)
(else 'UNKNOWN)))
(==> (bit-type 0) OFF)
(==> (bit-type 1) ON)
(==> (bit-type 25) UNKNOWN)
; https://groups.csail.mit.edu/mac/ftpdir/scheme-7.4/doc-html/scheme_13.html
; Universl Time https://www.gnu.org/software/mit-scheme/documentation/mit-scheme-ref/Universal-Time.html
(assert (integer? (get-universal-time)))
; https://www.gnu.org/software/mit-scheme/documentation/mit-scheme-ref/Procedure-Operations.html#Procedure-Operations
(assert (procedure? (lambda (x) x)))
(assert (compound-procedure? (lambda (x) x)))
(assert (not (compiled-procedure? (lambda (x) x))))
(assert (not (procedure? 3)))
(assert (= 18 (apply + (list 3 4 5 6))))
(assert (compiled-procedure? eval))
(let ((x "hello")
(y "world"))
(==> (string-append x y) "helloworld"))
(let* ((x 2)
(y (+ x 1)))
(==> (+ x y) 5))
(do ((i 0 (+ i 1)))
((>= i 10))
(assert (>= i 0))
(display i))
(==> (eval '(+ 2 2)) 4)
(==> (eval '(+ 2 2) (interaction-environment)) 4)
(assert (scheme-report-environment 5))
(assert (case (+ 2 3)
((2) #f)
((1 5) #t)))
(assert (case 7
((2) #f)
((1 5) #f)
(else #t)))
(assert (case 2
((2) #t)
((1 5) #f)
(else #f)))
(assert (letrec ((even?
(lambda (n)
(if (zero? n)
#t
(odd? (- n 1)))))
(odd?
(lambda (n)
(if (zero? n)
#f
(even? (- n 1))))))
(even? 88)))
(let ((x 0))
(inc! x)
(==> x 1)
(dec! x)
(==> x 0))
(let ((x 'A) (y 'B))
(swap! x y)
(==> x B)
(==> y A))
-37
View File
@@ -1,37 +0,0 @@
(let ((y "happy days")
(z #(1 2 3)))
(gc-flip)
(assert (string=? y "happy days"))
(assert (equal? z (vector 1 2 3))))
(let ((x 'HELLO)
(v #( HELLO ) ))
(display v)
(assert (eq? x (vector-ref v 0)))
(gc-flip)
(assert (eq? x (vector-ref v 0))))
(define counter 500)
(define big-vector '())
(define (basic-loop)
(begin
(set! big-vector (make-vector 200 0))
(set! counter (- counter 1))
(vector-fill! big-vector counter)
(gc-flip)
(assert (= (vector-ref big-vector 3) counter))
(if (> counter 0)
(basic-loop)
'())
) )
(basic-loop)
(==> (call/cc (lambda (throw) (define x '(1 2 3)) (gc-flip) (throw x))) (1 2 3))
(print-gc-statistics)
@@ -1,29 +0,0 @@
(define h (make-hash-table))
(assert (hash-table? h))
(hash-table-set! h 2000 1)
(assert (equal? (list (cons 2000 1)) (hash-table->alist h)))
(hash-table-set! h 2001 2)
(assert (equal? -1 (hash-table-ref h 3000 -1)))
(assert (equal? 1 (hash-table-ref h 2000 -1)))
(assert (equal? 2 (hash-table-ref h 2001 -1)))
(define h2 (alist->hash-table (hash-table->alist h)))
(assert (equal? -1 (hash-table-ref h2 3000 -1)))
(assert (equal? 1 (hash-table-ref h2 2000 -1)))
(assert (equal? 2 (hash-table-ref h2 2001 -1)))
(define h3 (alist->hash-table '((APPLE . "apple") (PEAR . "pear") (BANANA . "banana"))))
(assert (equal? "apple" (hash-table-ref h3 'APPLE)))
(assert (equal? "pear" (hash-table-ref h3 'PEAR)))
(assert (equal? '() (hash-table-ref h3 'HASH)))
@@ -1,160 +0,0 @@
; Scheme 9 from Empty Space, Function Library
; By Nils M Holm, 2009-2012
; Placed in the Public Domain
;
; (draw-tree object) ==> unspecific
; (dt) ==> unspecific
;
; Print a tree structure resembling a Scheme datum. Each cons
; cell is represented by [o|o] with lines leading to their car
; and cdr parts. Conses with a cdr value of () are represented
; by [o|/].
;
; DT is an abbrevation for DRAW-TREE.
;
; (Example): (draw-tree '((a) (b . c) (d e))) ==> unspecific
;
; Output: [o|o]---[o|o]---[o|/]
; | | |
; [o|/] | [o|o]---[o|/]
; | | | |
; a | d e
; |
; [o|o]--- c
; |
; b
(define (draw-tree n)
(define *nothing* (cons 'N '()))
(define *visited* (cons 'V '()))
(define (empty? x) (eq? x *nothing*))
(define (visited? x) (eq? (car x) *visited*))
(define (mark-visited x) (cons *visited* x))
(define (members-of x) (cdr x))
(define (done? x)
(and (pair? x)
(visited? x)
(null? (cdr x))))
(define (draw-fixed-string s)
(let* ((b (make-string 8 #\space))
(k (string-length s))
(s (if (> k 7) (substring s 0 7) s))
(s (if (< k 3) (string-append " " s) s))
(k (string-length s)))
(display (string-append s (substring b 0 (- 8 k))))))
(define (draw-atom n)
(cond ((null? n)
(draw-fixed-string "()"))
((symbol? n)
(draw-fixed-string (symbol->string n)))
((number? n)
(draw-fixed-string (number->string n)))
((string? n)
(draw-fixed-string (string-append "\"" n "\"")))
((char? n)
(draw-fixed-string (string-append "#\\" (string n))))
((eq? n #t)
(draw-fixed-string "#t"))
((eq? n #f)
(draw-fixed-string "#f"))
(else
(error "draw-atom: unknown type" n))))
(define (draw-conses n)
(let draw-conses ((n n)
(r '()))
(cond ((not (pair? n))
(draw-atom n)
(reverse! r))
((null? (cdr n))
(display "[o|/]")
(reverse! (cons (car n) r)))
(else
(display "[o|o]---")
(draw-conses (cdr n) (cons (car n) r))))))
(define (draw-bars n)
(let draw-bars ((n (members-of n)))
(cond ((not (pair? n)) '())
((empty? (car n))
(draw-fixed-string "")
(draw-bars (cdr n)))
((and (pair? (car n))
(visited? (car n)))
(draw-bars (members-of (car n)))
(draw-bars (cdr n)))
(else
(draw-fixed-string "|")
(draw-bars (cdr n))))))
(define (skip-empty n)
(if (and (pair? n)
(or (empty? (car n))
(done? (car n))))
(skip-empty (cdr n))
n))
(define (remove-trailing-nothing n)
(reverse (skip-empty (reverse n))))
(define (all-vertical? n)
(or (not (pair? n))
(and (null? (cdr n))
(all-vertical? (car n)))))
(define (draw-members n)
(let draw-members ((n (members-of n))
(r '()))
(cond ((not (pair? n))
(mark-visited
(remove-trailing-nothing
(reverse r))))
((empty? (car n))
(draw-fixed-string "")
(draw-members (cdr n)
(cons *nothing* r)))
((not (pair? (car n)))
(draw-atom (car n))
(draw-members (cdr n)
(cons *nothing* r)))
((null? (cdr n))
(draw-members (cdr n)
(cons (draw-final (car n)) r)))
((all-vertical? (car n))
(draw-fixed-string "[o|/]")
(draw-members (cdr n)
(cons (caar n) r)))
(else
(draw-fixed-string "|")
(draw-members (cdr n)
(cons (car n) r))))))
(define (draw-final n)
(cond ((not (pair? n))
(draw-atom n)
*nothing*)
((visited? n)
(draw-members n))
(else
(mark-visited (draw-conses n)))))
(if (not (pair? n))
(draw-atom n)
(let draw-tree ((n (mark-visited (draw-conses n))))
(if (not (done? n))
(begin (newline)
(draw-bars n)
(newline)
(draw-tree (draw-members n))))))
(newline))
(define dt draw-tree)
@@ -1,283 +0,0 @@
; Scheme 9 from Empty Space, Function Library
; By Nils M Holm, 1998-2009
; Placed in the Public Domain
;
; (prolog list1 list2) ==> list
; (new-database!) ==> unspecific
; (fact! list) ==> unspecific
; (predicate! list1 list2 ...) ==> unspecific
; (query list) ==> list
;
; (load-from-library "prolog.scm")
;
; This is a tiny PROLOG interpreter that is based on an even
; tinier PROLOG interpreter written in MACLISP by Ken Kahn.
;
; The PROLOG procedures takes a query LIST1 and a database
; LIST2 as arguments, attempts to prove LIST1 in LIST2, and
; returns the result(s).
; NEW-DATABASE! sets up a fresh PROLOG database (thereby
; deleting any existing one).
;
; FACT! adds a new fact to the database.
;
; PREDICATE! adds a predicate with the head LIST1 and the
; clauses LIST2 ... to the database.
;
; QUERY attempts to prove LIST1. It returns a list of results.
; An empty list indicates that LIST1 could not be proven.
;
; See "prolog-test.scm" for an example program.
;
; The following macros add some syntactic sugar for interactive
; use; they allows you to write, for instance, (! (man socrates))
; instead of (fact! '(man socrates)).
;
; (! fact) ==> unspecific
; (:- list1 list2 ...) ==> unspecific
; (? query) ==> unspecific
;
; The following special predicates are implemented in the
; interpreter: (== A B) returns a new environment if A can be
; unified with B, else NO. (Dif A B) returns NO if A can be
; unified with B, else YES (use only at the end of a clause!)
;
; Example: (begin (! (man socrates))
; (:- (mortal ?x)
; (man ?x))
; (query '(mortal ?who))) ==> (((who . socrates)))
(define *prolog-database* '())
(define (prolog q db)
(define empty-env '((())))
(define top-scope "")
(define true '(()))
(define false '())
(define (unique a)
(letrec
((unique2
(lambda (a r)
(cond ((null? a)
(reverse! r))
((member (car a) r)
(unique2 (cdr a) r))
(else
(unique2 (cdr a)
(cons (car a) r)))))))
(unique2 a '())))
(define (variable? x)
(and (symbol? x)
(char=? #\? (string-ref (symbol->string x) 0))))
(define (internal? x)
(and (symbol? x)
(char=? #\: (string-ref (symbol->string x) 0))))
(define (anonymous? x)
(eq? '_ x))
(define (extend n v env)
(cons (cons n v) env))
(define (new-scope env id)
(cond ((variable? env)
(string->symbol
(string-append (symbol->string env) id)))
((pair? env)
(cons (new-scope (car env) id)
(new-scope (cdr env) id)))
(else
env)))
(define (new-env-id x)
(string-append ";" x))
(define (value-of x env)
(if (variable? x)
(let ((v (assq x env)))
(if v
(value-of (cdr v) env)
x))
x))
(define (unify x y env)
(let ((x (value-of x env))
(y (value-of y env)))
(cond ((variable? x) (extend x y env))
((variable? y) (extend y x env))
((or (anonymous? x)
(anonymous? y))
env)
((and (pair? x)
(pair? y))
(let ((new (unify (car x) (car y) env)))
(and new (unify (cdr x) (cdr y) new))))
((eq? x y) env)
(else #f))))
(define (check-args g n)
(if (not (= n (length g)))
(error "wrong number of arguments" g)))
(define (goal-unify rules goals env id result)
(check-args (car goals) 3)
(let* ((this-goal (car goals))
(new-env (unify (cadr this-goal) (caddr this-goal) env)))
(if new-env
(let ((r (prove (cdr goals)
new-env
(new-env-id id))))
(try-rules (cdr rules) goals env id (append result r)))
(try-rules (cdr rules) goals env id result))))
(define (goal-dif rules goals env id result)
(check-args (car goals) 3)
(let* ((this-goal (car goals))
(new-env (unify (cadr this-goal) (caddr this-goal) env)))
(if (not new-env)
(let ((r (prove (cdr goals)
env
(new-env-id id))))
(try-rules (cdr rules) goals env id (append result r)))
false)))
(define (goal* rules goals env id result)
(let* ((this-rule (new-scope (car rules) id))
(new-env (unify (car goals) (car this-rule) env)))
(if new-env
(let ((r (prove (append (cdr this-rule) (cdr goals))
new-env
(new-env-id id))))
(try-rules (cdr rules) goals env id (append result r)))
(try-rules (cdr rules) goals env id result))))
(define (try-rules rules goals env id result)
(if (null? rules)
result
(case (caar goals)
((==) (goal-unify rules goals env id result))
((dif) (goal-dif rules goals env id result))
(else (goal* rules goals env id result)))))
(define (list-env env)
(letrec
((this-id caar)
(scope-id caddr)
(top-level?
(lambda (x)
(not (memv #\; (string->list (symbol->string x))))))
(var-name
(lambda (x)
(let* ((s (symbol->string x))
(k (string-length s)))
(let loop ((i 0))
(if (or (>= i k)
(char=? #\; (string-ref s i)))
(string->symbol (substring s 1 i))
(loop (+ 1 i)))))))
(list-env2
(lambda (e r)
(cond ((null? (cdr e))
(list r))
((top-level? (this-id e))
(list-env2 (cdr e)
(extend (var-name (this-id e))
(value-of (this-id e) env)
r)))
(else
(list-env2 (cdr e) r))))))
(list-env2 env '())))
; version without memoization
(define (prove goals env id)
(if (null? goals)
(list-env env)
(try-rules db goals env id '())))
;(define proven (make-hash-table))
;(define (prove goals env id)
; (if (null? goals)
; (list-env env)
; (let* ((k (append goals env))
; (v (hash-table-ref proven k #f)))
; (if v
; (car v)
; (let ((v (try-rules db goals env id '())))
; (hash-table-set! proven k v)
; v)))))
(define (any? p a)
(cond ((null? a) #f)
((p (car a)) #t)
(else (any? p (cdr a)))))
(define (cleanup env)
(apply append
(map (lambda (frame)
(if (or (any? (lambda (x) (variable? (cdr x))) frame)
(any? (lambda (x) (internal? (cdr x))) frame))
'()
(list frame)))
env)))
(cleanup (unique (prove (new-scope q top-scope)
empty-env
(new-env-id top-scope)))))
(define (new-database!)
(set! *prolog-database* '()))
(define (update! x)
(set! *prolog-database*
(cons x *prolog-database*)))
(define (fact! x)
(let ((update! update!))
(update! (list x))))
(define (predicate! head . clause*)
(let ((update! update!))
(update! (cons head clause*))))
(define (query . q)
(prolog q (reverse *prolog-database*)))
(define (print-frames env)
(cond ((equal? '(()) env)
(display "yes")
(newline))
((equal? '() env)
(display "no")
(newline))
(else
(for-each (lambda (frame)
(for-each (lambda (b)
(display (car b))
(display " = ")
(display (cdr b))
(display "; "))
frame)
(newline))
env))))
(define-macro ! (lambda (fact) `(fact! (quote ,fact))))
(define-macro :- (lambda args
(cons 'PREDICATE! (map1 (lambda (entry) `(quote ,entry)) args))))
(define-macro ? (lambda args
(list 'PRINT-FRAMES
(cons 'QUERY (map1 (lambda (entry) `(quote ,entry)) args)))))
-69
View File
@@ -1,69 +0,0 @@
(assert '())
(==> (cons 'a (cons 'b (cons 'c '()))) (a b c))
(==> (car (cons 1 2)) 1)
(==> (cdr (cons 1 2)) 2)
(==> (car (list 1 2)) 1)
(==> (cdr (list 1 2)) (2))
(define test-pair (cons 1 2))
(set-car! test-pair 3)
(set-cdr! test-pair 4)
(==> test-pair (3 . 4))
(==> (reverse '(a b c)) (c b a))
; https://groups.csail.mit.edu/mac/ftpdir/scheme-7.4/doc-html/scheme_8.html
(assert (pair? '(a . b)))
(assert (pair? '(a b c)))
(assert (not (pair? '())))
(assert (not (pair? '#(a b))))
(assert (= (length '(a b c)) 3))
(assert (= (length '()) 0))
(assert (not (null? '(a b c))))
(assert (null? '()))
(assert (eq? (list-ref '(a b c d) 2) 'c))
(==> (append '(a) '(b c d)) (a b c d))
(==> (sort '(1 4 2 6 3) <) (1 2 3 4 6))
(==> (make-list 4 1) (1 1 1 1))
(==> (memq 'a '(a b c)) (a b c))
(==> (memq 'b '(a b c)) (b c))
(==> (memq 'a '(b c d)) #f)
(==> (member (list 'a) '(b (a) c)) ((a) c))
(==> (member 'a '(b (a) c)) #f)
(assert (= (apply + (list 3 4 5 6)) 18))
(==> (append-reverse! '("y" "x" "w") '("z")) ("w" "x" "y" "z"))
; Association lists
(define list-map '((bob . 1) (john . 2) (dan . 3) (alice . 4)))
(assert (= (cdr (assoc 'john list-map)) 2))
(assert (= (cdr (assoc 'alice list-map)) 4))
(assert (not (assoc 'bad-key list-map)))
(assert (= (cdr (assq 'john list-map)) 2))
(assert (= (cdr (assq 'alice list-map)) 4))
(assert (not (assq 'bad-key list-map)))
(assert (list? '(1 2)))
(assert (not (list? (cons 1 2))))
(==> (reduce + 0 '(1 2 3 4)) 10)
(==> (reduce + 0 '(1 2)) 3)
(==> (reduce + 0 '()) 0)
(==> (reduce list '() '(1 2 3 4)) (((1 2) 3) 4))
(==> (fold-left + 0 '(1 2 3 4)) 10)
(==> (fold-left list '() '(1 2 3 4)) ((((() 1) 2) 3) 4))
-33
View File
@@ -1,33 +0,0 @@
; QUASIQUOTE
(assert (equal? `(1 2 3) '(1 2 3)))
(let ((x 1))
(assert (equal? `(,x 2 3) '(1 2 3))))
(let ((x 'a))
(assert (equal? `(,x x ,x) '(a x a))))
; nil! macro
(define-macro nil! (lambda (x)
`(set! ,x '())))
(define x 3)
(assert (= x 3))
(nil! x)
(assert (null? x))
; ntimes macro
(define-macro ntimes (lambda (n . body)
(let ((i (gensym)))
(cons 'DO (cons `((,i 0 (+ ,i 1)))
(cons `((>= ,i ,n) '()) body))
))))
(define x 0)
(ntimes 10 (set! x (+ x 1)))
(assert (= x 10))
-71
View File
@@ -1,71 +0,0 @@
; From Peter Norvig's Lispy tests
; http://norvig.com/lispy2.html
(define x 3)
(assert (= x 3))
(assert (= (+ x x) 6))
(assert (= (begin (define x 1) (set! x (+ x 1)) (+ x 1)) 3))
(assert (= ((lambda (x) (+ x x)) 5) 10))
(define twice (lambda (x) (* 2 x)))
(assert (= (twice 5) 10))
(define compose (lambda (f g) (lambda (x) (f (g x)))))
(assert (= (car ((compose list twice) 5)) 10))
(define repeat (lambda (f) (compose f f)))
(assert (= ((repeat twice) 5) 20))
(assert (= ((repeat (repeat twice)) 5) 80))
(define fact (lambda (n) (if (< n 2) 1 (* n (fact (- n 1))))))
(assert (= (fact 3) 6))
(define abs (lambda (n) ((if (> n 0) + -) 0 n)))
(assert (= (car (list (abs -3) (abs 0) (abs 3))) 3))
(define combine (lambda (f)
(lambda (x y)
(if (null? x) (quote ())
(f (list (car x) (car y))
((combine f) (cdr x) (cdr y)))))))
(define zip (combine cons))
(assert (= (car (cdr (assoc 3 (zip (list 1 2 3 4) (list 5 6 7 8))))) 7))
(define riff-shuffle (lambda (deck) (begin
(define take (lambda (n seq) (if (< n 1) (quote ()) (cons (car seq) (take (- n 1) (cdr seq))))))
(define drop (lambda (n seq) (if (< n 1) seq (drop (- n 1) (cdr seq)))))
(define mid (lambda (seq) (/ (length seq) 2)))
((combine append) (take (mid deck) deck) (drop (mid deck) deck)))))
(display (riff-shuffle (list 1 2 3 4 5 6 7 8)))
(newline)
(display ((repeat riff-shuffle) (list 1 2 3 4 5 6 7 8)))
(newline)
(display (riff-shuffle (riff-shuffle (riff-shuffle (list 1 2 3 4 5 6 7 8)))))
(newline)
(define fabs (lambda (n) ((if (> n 0.0) + -) 0.0 n)))
(define (newton guess function derivative epsilon)
(define guess2 (- guess (/ (function guess) (derivative guess))))
(if (< (fabs (- guess guess2)) epsilon) guess2
(newton guess2 function derivative epsilon)))
(define (square-root a)
(newton 1.0 (lambda (x) (- (* x x) a)) (lambda (x) (* 2.0 x)) 0.0001))
(display "sqrt(2)=")
(display (square-root 2.0))
(newline)
(display "sqrt(200)=")
(display (square-root 200.0))
(newline)
(==> (call/cc (lambda (throw) (+ 5 (* 10 (throw 1))))) 1)
(==> (call/cc (lambda (throw) (+ 5 (* 10 1)))) 15)
(==> (call/cc (lambda (throw) (+ 5 (* 10 (call/cc (lambda (escape) (* 100 (escape 3)))))))) 35)
(==> (call/cc (lambda (throw) (+ 5 (* 10 (call/cc (lambda (escape) (* 100 (throw 3)))))))) 3)
(==> (call/cc (lambda (throw) (+ 5 (* 10 (call/cc (lambda (escape) (* 100 1))))))) 1005)
(==> (let ((a 1) (b 2)) (+ a b)) 3)
-82
View File
@@ -1,82 +0,0 @@
(==> (+ 2 2) 4)
(==> (+ (* 2 100) (* 1 10)) 210)
(==> (if (> 6 5) (+ 1 1) (+ 2 2)) 2)
(==> (if (< 6 5) (+ 1 1) (+ 2 2)) 4)
(==> (gcd 32 -36) 4)
(==> (gcd 4 3) 1)
(==> (gcd) 0)
(==> (lcm 32 -36) 288)
(assert (exact? (lcm 32 -36)))
(assert (inexact? (lcm 32.0 -36)))
(==> (lcm) 1)
(==> (abs -1) 1)
(==> (map + '(1 1 1) '(2 2 2)) (3 3 3))
(==> (map abs '(-1 -2 3)) (1 2 3))
(==> (vector-map abs #(-1 -2 3)) #(1 2 3))
(==> (- 1) -1)
(==> (- 436) -436)
(==> (- -7) 7)
(assert (integer? 3))
(assert (real? 3))
(assert (real? 3.5))
(assert (not (integer? 3.5)))
(assert (< 3 4))
(assert (> 4 3))
(assert (>= 4 3))
(assert (<= 3 4))
(assert (<= 1 1))
(assert (< -5 5))
(assert (not (> 3 4)))
(assert (= (modulo -13 4) 3))
(assert (= (remainder -13 4) -1))
(assert (= (remainder 13 -4) 1))
(assert (even? 2))
(assert (not (odd? 2)))
(assert (odd? 3))
(assert (odd? 7))
(assert (not (odd? 4)))
(assert (exact? (+ 1 2 3)))
(assert (inexact? (+ 1 2.5 3)))
(assert (inexact? (+ 1.3 2 3)))
(assert (exact? (* 1 2 3)))
(assert (inexact? (* 1 2.5 3)))
(assert (inexact? (* 1.3 2 3)))
(assert (exact? (- 1 2)))
(assert (inexact? (- 1 2.5)))
(assert (inexact? (- 1.3 2)))
(assert (exact? (expt 3 3)))
(==> (expt 3 3) 27)
(assert (inexact? (expt 3 2.5)))
(==> (magnitude 13) 13)
(==> (magnitude -13) 13)
(==> (floor 0.87) 0)
(==> (ceiling 0.87) 1)
(==> (round 0.87) 1)
(assert (< (- (abs (atan 0)) (/ 3.141592 4)) 0.001))
-21
View File
@@ -1,21 +0,0 @@
(define (brute max-length set)
(define (permute n)
(define str (make-string n))
(define (iter d)
(if (= d n)
(begin
(display str)
(display " "))
(do ((i 0 (+ i 1)))
((= i (string-length set)) 'done)
(begin
(string-set! str d (string-ref set i))
(iter (+ d 1))))))
(iter 0))
(do ((len 0 (+ len 1)))
((> len max-length) 'done)
(permute len)))
(brute 4 "abcd")
(newline)
-5
View File
@@ -1,5 +0,0 @@
(include "include/prolog.scm")
(! (man socrates))
(:- (mortal ?x) (man ?x))
(? (mortal ?who))
-158
View File
@@ -1,158 +0,0 @@
; A collection of SICP excercises
; these are all my solutions from reading
; the book
; 1.01 - basic expressions
(assert (= (+ (* 2 4) (- 4 6)) 6))
(define a 3)
(define b (+ a 1))
(assert (= (+ a b (* a b)) 19))
(assert (= (if (and (> b a) (< b (* a b)))
b
a) 4))
(assert (= (cond ((= a 4) 6)
((= b 4) (+ 6 7 a))
(else 25)) 16))
(assert (= (+ 2 (if (> b a) b a )) 6))
(assert (= (* (cond ((> a b) a)
((< a b) b)
(else -1))
(+ a 1)) 16))
; 1.03 - largest squares
(define (sqr x) (* x x))
(define (largest-squares x y z)
(cond ((and (< z x) (< z y)) (+ (sqr x) (sqr y)))
((and (< y x) (> y z)) (+ (sqr x) (sqr z)))
(else (+ (sqr y) (sqr z)))
))
(assert (= (largest-squares 3 4 5) (+ 25 16)))
(assert (= (largest-squares 3 5 4) (+ 25 16)))
; 1.14 change counter
(define (count-change amount) (cc amount 5))
(define (cc amount kinds-of-coins)
(cond ((= amount 0) 1)
((or (< amount 0) (= kinds-of-coins 0)) 0)
(else (+ (cc amount
(- kinds-of-coins 1))
(cc (- amount
(first-denomination kinds-of-coins))
kinds-of-coins)))))
(define (first-denomination kinds-of-coins)
(cond ((= kinds-of-coins 1) 1)
((= kinds-of-coins 2) 5)
((= kinds-of-coins 3) 10)
((= kinds-of-coins 4) 25)
((= kinds-of-coins 5) 50)))
(display "counting change: ")
(display (count-change 75))
(newline)
; 1.16 - fast powers
(define (exp-fast b n) (exp-iter b n 1))
(define (exp-iter b n product)
(cond ((= n 0) product)
; b^n = (b^2) n/2
((even? n) (exp-iter (* b b) (/ n 2) product))
; b^n = b * b^n-1
(else (exp-iter b (- n 1) (* product b)))))
(assert (= (exp-fast 5 4) 625))
(assert (= (exp-fast 2 8) 256))
; 1.17 - fast multiply
(define (double a) (+ a a))
(define (halve a) (/ a 2))
(define (fast-mul a b) (fast-mul-iter a b 0))
(define (fast-mul-iter a b sum)
(cond ((= b 0) sum)
((even? b) (fast-mul-iter (double a) (halve b) sum))
(else (fast-mul-iter a (- b 1) (+ sum a)))))
(assert (= (fast-mul 3 4) 12))
(assert (= (fast-mul 100 10) 1000))
; 1.19 fibonacci
(define (fib-helper n a b p q)
(cond
((= n 0) b)
((even? n) (fib-helper (/ n 2)
a
b
(+ (* p p) (* q q))
(+ (* 2 q p) (* q q ))
))
(else
(fib-helper
(- n 1)
(+ (* b q) (* a q) (* a p))
(+ (* b p) (* a q))
p
q))))
(define (fib n)
(fib-helper n 1 0 0 1))
(assert (= (fib 5) 5))
(assert (= (fib 7) 13))
(assert (= (fib 8) 21))
; 2.21 - square list
(define (square-list items)
(if (null? items)
items
(cons (* (car items) (car items)) (square-list (cdr items)))))
(define (square-list2 items)
(map (lambda (x) (* x x)) items))
(display (square-list (list 1 2 3 4)))
(newline)
(display (square-list2 (list 4 5 6 7)))
; bank accounts
(define (make-account val)
(lambda (action)
(if (eq? action 'deposit)
(lambda (n) (set! val (+ val n)))
(lambda (n) (set! val (- val n))))))
(define justin (make-account 100))
(define ryan (make-account 200))
((justin 'deposit) 20)
((ryan 'withdraw) 20)
(gc-flip)
(assert (= ((justin 'withdraw) 0) 120))
(assert (= ((ryan 'deposity) 0) 180))
; and or expansion
(let ((a 1))
(if (and (= a 0) (garbage here))
(assert 0)
'pass)
(if (or (= a 1) (garbage here))
'pass
(assert 0)))
-32
View File
@@ -1,32 +0,0 @@
(==> (force (delay (+ 1 2))) 3)
(==> (let ((p (delay (+ 1 2))))
(list (force p) (force p))) (3 3))
(assert (promise? (delay (+ 1 2))))
; promises computed at most once
(define count 0)
(define p
(delay
(begin
(set! count (+ count 1))
(* x 3))))
(define x 5)
(==> count 0)
(assert (promise? p))
(==> (force p) 15)
(assert (promise? p))
(==> count 1)
(==> (force p) 15)
(==> count 1)
(define (integers-starting-from n)
(cons-stream n (integers-starting-from (+ n 1))))
(assert (equal? (stream-head (integers-starting-from 0) 5) '(0 1 2 3 4)))
-60
View File
@@ -1,60 +0,0 @@
; https://groups.csail.mit.edu/mac/ftpdir/scheme-7.4/doc-html/scheme_7.html
; TODO: add characters to reader
(==> (make-string 10 #\x) "xxxxxxxxxx")
(assert (string? "Hi"))
(assert (not (string? 'Hi)))
(==> (string-length "") 0)
(==> (string-length "The length") 10)
(assert (string=? "PIE" "PIE"))
(assert (not (string=? "PIE" "pie")))
(==> (list->string (string->list "hello 123")) "hello 123")
(==> (string->list (list->string '(#\A #\B #\3))) (#\A #\B #\3))
; https://www.gnu.org/software/mit-scheme/documentation/mit-scheme-ref/Symbols.html
(assert (symbol? 'foo))
(assert (symbol? (car '(a b))))
(assert (not (symbol? "bar")))
(assert (eq? 'foo (string->symbol "FOO")))
(assert (string=? "FLYING-FISH" (symbol->string 'flying-fish)))
; specials
(==> (string-length "\\") 1)
(==> (string-length "\t") 1)
(==> (string-length "\n") 1)
(==> (string-length "\f") 1)
(==> (string-length "\"") 1)
(display "Hello\nworld!")
(==> (string->number (number->string 279)) 279)
(==> (number->string (string->number "279")) "279")
(==> (string->number (number->string 0.5)) 0.5)
(assert (symbol<? 'A 'B))
(assert (not (symbol<? 'WALK 'DOG)))
(==> (- (char->integer #\c) (char->integer #\a)) 2)
(==> (string-ref "abc" 0) #\a)
(==> (string-ref "abc" 2) #\c)
(==> (string #\a #\b) "ab")
(==> (string) "")
(assert (char<? #\a #\b))
(assert (char<=? #\a #\a))
(assert (char-lower-case? #\a))
(assert (not (char-lower-case? #\A)))
(assert (not (char-upper-case? #\c)))
(assert (char-upper-case? #\C))
(assert (char-ci=? #\a #\A))
(assert (char-ci<? #\A #\b))
-75
View File
@@ -1,75 +0,0 @@
(define v #(1 2 3))
(vector-swap! v 0 2)
(assert (= 3 (vector-ref v 0)))
(assert (= 1 (vector-ref v 2)))
(define (vec-sorted? v op)
; "if x and y are any two adjacent elements in the result,
; where x precedes y, it is the case that (procedure y x) ==> #f"
; https://groups.csail.mit.edu/mac/ftpdir/scheme-7.4/doc-html/scheme_8.html#SEC72
(or (< (vector-length v) 2)
(and (not (op (vector-ref v 1) (vector-ref v 0)))
(vec-sorted? (vector-tail v 1) op))))
; First make sure our sorted checker works
(assert (vec-sorted? #(1 2 2 4 5 6) <))
(assert (vec-sorted? #(1) <))
(assert (vec-sorted? #(1 2) <))
(assert (vec-sorted? #(7 6 5 4 3 2 1) >))
(assert (not (vec-sorted? #(2 1) <)))
(assert (not (vec-sorted? #(1 2 3 4 4 3) <)))
(assert (not (vec-sorted? #(1 2 3 2 4 5) <)))
; Now test the sort function
(assert (vec-sorted? (sort! #(1) <) <))
(assert (vec-sorted? (sort! #(2 1) <) <))
(assert (vec-sorted? (sort! #(1 2 3) <) <))
(assert (vec-sorted? (sort! #(3 8 1 7 2 9 4 5) <) <))
(assert (vec-sorted? (sort! #(1 2 3 4 5 6 7 8) <) <))
(assert (vec-sorted? (sort! #(3 8 1 7 2 9 4 5) >) >))
(assert (vec-sorted? (sort! #(1 2 3 4 5 6 7 8) >) >))
(assert (vec-sorted? (sort! #(92 59 30 57 74 78 43 33 77 10 78 83 76 49 42 94 82 70 15 11 90 86 44 70 39 64 69 30 59 95 15 79 13 54 98 82 42 96 79 17 56 93 20 1 84 72 75 19 74 43) >) >))
(assert (vec-sorted? (sort! #(92 59 30 57 74 78 43 33 77 10 78 83 76 49 42 94 82 70 15 11 90 86 44 70 39 64 69 30 59 95 15 79 13 54 98 82 42 96 79 17 56 93 20 1 84 72 75 19 74 43) <) <))
(assert (vec-sorted? (sort! #(3 8 1 7 2 9 4 5) <) <))
; Try other data types
(assert (vec-sorted? (sort! #(#\C #\B #\A #\D) char<?) char<?))
; Converting between lists and vectors
;https://www.gnu.org/software/mit-scheme/documentation/stable/mit-scheme-ref/Construction-of-Vectors.html
(==> (vector 'a 'b 'c) #(A B C))
(==> (list->vector '(dididit dah)) #(dididit dah))
; Binary search
(assert (= (vector-binary-search #(1 2 3 4 5) < (lambda (x) x) 3) 3))
(assert (not (vector-binary-search #(1 2 2 4 5) < (lambda (x) x) 3)))
(define v (vector 1 1 2))
(vector-fill! v 3)
(==> v #(3 3 3))
(==> (make-initialized-vector 5 (lambda (x) (* x x))) #(0 1 4 9 16))
(==> (vector-head #(1 2 3) 2) #(1 2))
; Issues parsing large vector
(define big-v #(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200))
(==> (vector-length big-v) 200)
; Subvector
(==> (subvector #(1 2 3 4) 1 4) #(2 3 4))
(==> (subvector #(1 2 3 4) 0 2) #(1 2))
(==> (subvector #(A 1 A 1 A 1 A 1) 1 3) #(1 A))
; Association
(define avector #((bob . 1) (john . 2) (dan . 3) (alice . 4)))
(assert (= (cdr (vector-assq 'john avector)) 2))
(assert (= (cdr (vector-assq 'alice avector)) 4))
(assert (not (vector-assq 'bad-key avector)))
(sort! (make-initialized-vector 10000 (lambda (x) (random 1000000))) <)
-20
View File
@@ -1,20 +0,0 @@
; try this one with read
(let* ((file (open-input-file "big_data_gen.sexpr"))
(data (read file)))
(gc-flip)
(print-gc-statistics)
(newline)
(display "records: ")
(display (length data))
(newline)
(let ((record (car data)))
(assert (= (cdr (vector-assq 'index record)) 0))
(assert (eq? (cdr (vector-assq 'isActive record)) 'False))
(assert (= (cdr (vector-assq 'age record)) 21)))
(close-input-port file))
(display "done")
(newline)
-12
View File
@@ -1,12 +0,0 @@
; canada
(let ((data (read)))
(gc-flip)
(print-gc-statistics)
(newline)
(display "records: ")
(display (vector-length data)))
(gc-flip)
(print-gc-statistics)
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
@@ -1,38 +0,0 @@
import json
import sys
import itertools
json_data = json.load(sys.stdin)
def lisp_dump(data):
if isinstance(data, int):
return str(data)
elif isinstance(data, float):
return str(data)
elif isinstance(data, str):
escaped_string = data.encode("unicode_escape").decode("utf-8")
return "\"" + escaped_string + "\""
elif isinstance(data, list):
result = "("
for i, item in enumerate(data):
result += lisp_dump(item)
if i + 1 < len(data):
result += " "
result += ")"
return result
elif isinstance(data, dict):
result = "#("
i = 0
for key, item in data.items():
result += "(%s . %s)" % (key, lisp_dump(item))
if i + 1 < len(data):
result += " "
i += 1
result += ")"
return result
else:
print("error")
print(type(data))
print(lisp_dump(json_data))
-4
View File
@@ -1,4 +0,0 @@
#!/bin/sh
../../lisp --script big_data1.scm
cat big_data_canada.sexpr | ../../lisp --script big_data2.scm
@@ -1,81 +0,0 @@
(define (quasiquote-helper tail x)
(cond ((null? x) (reverse! tail))
((not (pair? x)) (list 'QUOTE x))
((eq? 'UNQUOTE (car x)) (car (cdr x)))
((eq? 'UNQUOTESPLICE (car x)) (error "invalid place"))
((and (pair? (car x))
(eq? (car (car x)) 'UNQUOTESPLICE))
(quasiquote-helper (reverse-append! (car (cdr (car x))) tail) (cdr x))
)
(else
(quasiquote-helper (cons (quasiquote-helper '() (car x)) tail)
(cdr x)))
))
(define-macro quasiquote
(lambda (x)
(display x)
(newline)
(quasiquote-helper '() x)
))
(display (macroexpand '`(1 ,x 3)))
(newline)
;(display (macroexpand '`(1 ,@(2 2) 3)))
(define-macro do2
(lambda (vars loop-check loop)
(let ((names '())
(inits '())
(steps '())
(func (gensym)))
(for-each (lambda (var)
(push (car var) names)
(set! var (cdr var))
(push (car var) inits)
(set! var (cdr var))
(push (car var) steps))
vars)
(display loop-check)
(newline)
`((lambda (,func)
(begin
(set! ,func (lambda ,names
(if ,(car loop-check)
,(car (cdr loop-check))
,(cons 'BEGIN (list loop (cons func steps)))
)))
,(cons func inits)
)) '())
)))
(display (macroexpand '(do2 ((i 0 (+ i 1)))
((> i 0) 'done)
'())))
(newline)
(newline)
(define-macro let2 (lambda (def-list . body)
(cons `(lambda
,(map1 (lambda (entry) (car entry)) def-list '())
,(cons 'BEGIN body))
(map1 (lambda (entry) (car (cdr entry))) def-list '())) ))
(display (macroexpand
'(let2 ((x 1) (y 2)) (set! x (+ x y)) x)))
@@ -1,2 +0,0 @@
temp1.scm
temp2.scm
-19
View File
@@ -1,19 +0,0 @@
(book
(author "ARISTOTLE")
(title "Categories")
(year -350)
(tags #(philosophy logic definitions substance))
(lines
"Things are said to be named \"equivocally\" when, though they have a common name, the definition corresponding with the name differs for each."
"Thus, a real man and a figure in a picture can both lay claim to the name \"animal\"; yet these are equivocally so named, for, though they have a common name, the definition corresponding with the name differs for each"
"For should any one define in what sense each is an animal, his definition in the one case will be appropriate to that case only.\n"
"On the other hand, things are said to be named \"univocally\" which have both the name and the definition answering to the name in common"
"A man and an ox are both \"animal\", and these are univocally so named, inasmuch as not only the name, but also the definition, is the same in both cases:"
"for if a man should state in what sense each is an animal, the statement in the one case would be identical with that in the other.\n"
"Things are said to be named \"derivatively\", which derive their name from some other name, but differ from it in termination."
"Thus the grammarian derives his name from the word \"grammar\", and the courageous man from the word \"courage\"."
))
(alphabet #\a #\b #\c #\d #\e)
(nums 0.0 0.25 0.5 0.75 1.0)
-14
View File
@@ -1,14 +0,0 @@
#!/bin/sh
# make sure reader and writer work and are compatible.
# 1. load s expresion, print it out.
cat sample.scm | ../../printer > temp1.scm
# 2. load the output, print it out again.
cat temp1.scm | ../../printer > temp2.scm
# 3. ensure both outputs match
cmp temp1.scm temp2.scm
+18 -1
View File
@@ -5,6 +5,9 @@
* interactions. \version 0.1 \date 21 septembre 2024 * interactions. \version 0.1 \date 21 septembre 2024
*/ */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> #include <unistd.h>
#define _DEFAULT_SOURCE #define _DEFAULT_SOURCE
@@ -18,18 +21,32 @@
#include "include/output.h" #include "include/output.h"
#include "include/terminal.h" #include "include/terminal.h"
#include <locale.h>
#include <wchar.h>
struct editorConfig E; struct editorConfig E;
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
char * splash_screen = (char *) calloc(256, sizeof(char));
// Set support for utf-8
setlocale(LC_ALL, "");
// INIT
enableRawMode(); enableRawMode();
initEditor(); initEditor();
if (argc >= 2) { if (argc >= 2) {
E.state = READ_AND_WRITE; E.state = READ_AND_WRITE;
editorOpen(argv[1]); editorOpen(argv[1]);
} else { } else {
editorOpen("assets/beluga.txt"); strcat(splash_screen, getenv("HOME"));
strcat(splash_screen, "/.beluga/assets/beluga.txt");
fprintf(stderr, "%s\n", splash_screen);
editorOpen(splash_screen);
} }
free(splash_screen);
editorSetStatusMessage("HELP: Ctrl-S = save | Ctrl-Q = quit"); editorSetStatusMessage("HELP: Ctrl-S = save | Ctrl-Q = quit");
-4
View File
@@ -8,9 +8,6 @@ project('beluga', 'c',
cc = meson.get_compiler('c') cc = meson.get_compiler('c')
m = cc.find_library('m', required: false) m = cc.find_library('m', required: false)
# Include directory
inc_dir = include_directories('include', 'lisp-interpreter/dist')
# Source files # Source files
src_files = files( src_files = files(
'main.c', 'main.c',
@@ -28,6 +25,5 @@ src_files = files(
# Executable # Executable
executable('beluga', executable('beluga',
src_files, src_files,
include_directories : inc_dir,
dependencies: [m] dependencies: [m]
) )
+2 -2
View File
@@ -2,8 +2,8 @@
extern struct editorConfig E; extern struct editorConfig E;
void abAppend(struct abuf *ab, const char *s, int len) { void abAppend(struct abuf *ab, const unsigned char *s, int len) {
char *new = realloc(ab->b, ab->len + len); unsigned char *new = realloc(ab->b, ab->len + len);
if (new == NULL) { if (new == NULL) {
return; return;
+68 -14
View File
@@ -3,23 +3,33 @@
#include "../include/input.h" #include "../include/input.h"
#include "../include/file_io.h" #include "../include/file_io.h"
#include "../include/editor_op.h" #include "../include/editor_op.h"
#include "../include/row_op.h"
#include "../include/data.h" #include "../include/data.h"
#include "../include/terminal.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
utf_8_char_t make_utf8_char(const char *bytes, int len) {
utf_8_char_t ch;
ch.len = len;
memcpy(ch.c, bytes, len);
return ch;
}
Lisp mapKey(Lisp args, LispError *e, LispContext ctx) { Lisp mapKey(Lisp args, LispError *e, LispContext ctx) {
const char *key_sequence = lisp_string(lisp_car(args)); const char *key_string = lisp_string(lisp_car(args));
KeyInfo *key = stringToCodepoint(key_string);
args = lisp_cdr(args); args = lisp_cdr(args);
// second argument // second argument
Lisp func = lisp_car(args); Lisp func = lisp_car(args);
E.key_binds = E.key_binds =
(struct keyBind_t *)realloc(E.key_binds, ++E.number_of_keybinds * sizeof(struct keyBind_t)); (struct keyBind_t *)realloc(E.key_binds, ++E.number_of_keybinds * sizeof(struct keyBind_t));
E.key_binds[E.number_of_keybinds - 1].key_sequence = (char *) malloc(50 * sizeof(char)); E.key_binds[E.number_of_keybinds - 1].key_sequence = (KeyInfo *) malloc(sizeof(KeyInfo));
strncpy(E.key_binds[E.number_of_keybinds - 1].key_sequence, key_sequence, 50); memcpy(E.key_binds[E.number_of_keybinds - 1].key_sequence, key, sizeof(KeyInfo));
E.key_binds[E.number_of_keybinds - 1].command = func; E.key_binds[E.number_of_keybinds - 1].command = func;
@@ -27,28 +37,31 @@ Lisp mapKey(Lisp args, LispError *e, LispContext ctx) {
} }
Lisp moveCursor(Lisp args, LispError *e, LispContext ctx) { Lisp moveCursor(Lisp args, LispError *e, LispContext ctx) {
fprintf(stderr, "Cursor is moving\n");
const char *direction = lisp_string(lisp_car(args)); const char *direction = lisp_string(lisp_car(args));
KeyInfo key;
key.type = KEY_ARROW;
switch (direction[0]) { switch (direction[0]) {
case 'u': case 'u':
editorMoveCursor(ARROW_UP); key.data.arrow = 'A';
break; break;
case 'd': case 'd':
editorMoveCursor(ARROW_DOWN); key.data.arrow = 'B';
break; break;
case 'r': case 'r':
editorMoveCursor(ARROW_RIGHT); key.data.arrow = 'C';
break; break;
case 'l': case 'l':
editorMoveCursor(ARROW_LEFT); key.data.arrow = 'D';
break; break;
} }
editorMoveCursor(&key);
return lisp_null(); return lisp_null();
} }
Lisp editorQuit(Lisp args, LispError* e, LispContext ctx) { Lisp editorQuit(Lisp args, LispError* e, LispContext ctx) {
fprintf(stderr, "quit\n");
if (E.dirty && E.quit_times_buffer > 0) { if (E.dirty && E.quit_times_buffer > 0) {
editorSetStatusMessage("WARNING! Changes hasn't been saved. Press Ctrl-Q " editorSetStatusMessage("WARNING! Changes hasn't been saved. Press Ctrl-Q "
"another time to quit."); "another time to quit.");
@@ -77,7 +90,7 @@ Lisp l_editorSave(Lisp args, LispError* e, LispContext ctx) {
Lisp l_editorInsertNewLine(Lisp args, LispError* e, LispContext ctx) { Lisp l_editorInsertNewLine(Lisp args, LispError* e, LispContext ctx) {
editorInsertNewLine(); // editorInsertNewLine();
return lisp_null(); return lisp_null();
@@ -105,8 +118,11 @@ Lisp deletePreviousChar(Lisp args, LispError* e, LispContext ctx) {
Lisp editorMoveCursorPageUp(Lisp args, LispError* e, LispContext ctx) { Lisp editorMoveCursorPageUp(Lisp args, LispError* e, LispContext ctx) {
E.cursor_y = E.row_offset; E.cursor_y = E.row_offset;
int times = E.screenrows; int times = E.screenrows;
KeyInfo key;
key.type = KEY_ARROW;
key.data.arrow = 'D';
while (--times) { while (--times) {
editorMoveCursor(ARROW_UP); editorMoveCursor(&key);
} }
return lisp_null(); return lisp_null();
} }
@@ -117,15 +133,18 @@ Lisp editorMoveCursorPageDown(Lisp args, LispError* e, LispContext ctx) {
E.cursor_y = E.numrows; E.cursor_y = E.numrows;
} }
int times = E.screenrows; int times = E.screenrows;
KeyInfo key;
key.type = KEY_ARROW;
key.data.arrow = 'D';
while (--times) { while (--times) {
editorMoveCursor(ARROW_DOWN); editorMoveCursor(&key);
} }
return lisp_null(); return lisp_null();
} }
Lisp editorOpenFile(Lisp args, LispError *e, LispContext ctx) { Lisp editorOpenFile(Lisp args, LispError *e, LispContext ctx) {
char *filename = editorPrompt("Path : %s"); char *filename = editorPrompt("Open : %s", getenv("PWD"), 1);
if (filename) if (filename)
editorOpen(filename); editorOpen(filename);
@@ -134,8 +153,43 @@ Lisp editorOpenFile(Lisp args, LispError *e, LispContext ctx) {
Lisp editorPrintC(Lisp args, LispError *e, LispContext ctx) { Lisp editorPrintC(Lisp args, LispError *e, LispContext ctx) {
char c = lisp_char(lisp_car(args)); char *c = lisp_string(lisp_car(args));
editorInsertChar(c); utf_8_char_t ch = make_utf8_char(c, 1);
editorInsertChar(&ch);
return lisp_null();
}
Lisp addPackage(Lisp args, LispError *e, LispContext ctx) {
const char *package_name = lisp_string(lisp_car(args));
char *package_dir = (char *) calloc(256, sizeof(char));
FILE *fd_package = NULL;
strcat(package_dir, getenv("HOME"));
strcat(package_dir, "/.beluga/packages/");
strcat(package_dir, package_name);
strcat(package_dir, "/init.lisp");
fd_package = fopen(package_dir, "r");
lisp_eval(lisp_read_file(fd_package, &E.ctx_error, E.ctx), &E.ctx_error,
E.ctx);
fclose(fd_package);
free(package_dir);
return lisp_null();
}
Lisp editorDelRow_L(Lisp args, LispError *e, LispContext ctx) {
editorDelRow(E.cursor_y);
return lisp_null();
}
Lisp editorFind_L(Lisp args, LispError *e, LispContext ctx) {
editorFind();
return lisp_null();
}
Lisp editorReadChar_L(Lisp args, LispError *e, LispContext ctx) {
// fprintf(stderr, "char read : %c\n", E.row[E.cursor_y].render[E.cursor_x]);
// return lisp_make_char(E.row[E.cursor_y].render[E.cursor_x]);
return lisp_null(); return lisp_null();
} }
+71 -25
View File
@@ -1,48 +1,94 @@
#include "../include/editor_op.h" #include "../include/editor_op.h"
#include "../include/row_op.h" #include "../include/row_op.h"
#include "data.h" #include "include/data.h"
#include <stdio.h>
extern struct editorConfig E; extern struct editorConfig E;
void editorInsertChar(int c) { void editorInsertChar(utf_8_char_t *c) {
if (E.cursor_y == E.numrows) { if (E.state == READ_ONLY) return;
editorInsertRow(E.numrows, "", 0); fprintf(stderr, "Insert char %s %d\n", c->c, c->len);
} // If cursor is past end of file, add empty rows
editorRowInsertChar(&E.row[E.cursor_y], E.cursor_x, c); if (E.cursor_y == E.numrows) {
E.cursor_x++; editorInsertRow(E.numrows, "", 0);
}
// Insert character at cursor position
editorRowInsertChar(&E.row[E.cursor_y], E.cursor_x, *c);
E.cursor_x++;
} }
void editorInsertNewLine() { void editorInsertNewline(void) {
erow *row; if (E.state == READ_ONLY) return;
if (!E.cursor_x) {
if (E.cursor_x == 0) {
// Insert blank line before current line
editorInsertRow(E.cursor_y, "", 0); editorInsertRow(E.cursor_y, "", 0);
} else { } else {
row = &E.row[E.cursor_y]; // Split current line at cursor
editorInsertRow(E.cursor_y + 1, &row->chars[E.cursor_x], erow *row = &E.row[E.cursor_y];
row->size - E.cursor_x);
row = &E.row[E.cursor_y]; // Calculate byte length of remaining part
int remaining_chars = row->size - E.cursor_x;
// Allocate buffer for remaining characters
char *buf = malloc(remaining_chars * 4); // Max 4 bytes per UTF-8 char
int buf_len = 0;
// Convert utf_8_char_t to bytes
for (int i = E.cursor_x; i < row->size; i++) {
for (int j = 0; j < row->chars[i].len; j++) {
buf[buf_len++] = row->chars[i].c[j];
}
}
// Insert new row with remaining text
editorInsertRow(E.cursor_y + 1, buf, buf_len);
free(buf);
// Truncate current row at cursor
row = &E.row[E.cursor_y]; // Refresh pointer after realloc
row->size = E.cursor_x; row->size = E.cursor_x;
row->chars[row->size] = '\0';
editorUpdateRow(row); editorUpdateRow(row);
} }
++E.cursor_y;
E.cursor_y++;
E.cursor_x = 0; E.cursor_x = 0;
} }
void editorDelChar() { void editorRowAppendRow(erow *dest, erow *src) {
erow *row; // Allocate space for combined rows
if (E.cursor_y == E.numrows || !(E.cursor_x || E.cursor_y)) { utf_8_char_t *new_chars = realloc(dest->chars,
return; sizeof(utf_8_char_t) * (dest->size + src->size));
} if (!new_chars) return;
row = &E.row[E.cursor_y];
dest->chars = new_chars;
// Copy source row characters
memcpy(&dest->chars[dest->size], src->chars, sizeof(utf_8_char_t) * src->size);
dest->size += src->size;
editorUpdateRow(dest);
++E.dirty;
}
void editorDelChar(void) {
if (E.state == READ_ONLY) return;
if (E.cursor_y == E.numrows) return;
if (E.cursor_x == 0 && E.cursor_y == 0) return;
erow *row = &E.row[E.cursor_y];
if (E.cursor_x > 0) { if (E.cursor_x > 0) {
// Delete character before cursor
editorRowDelchar(row, E.cursor_x - 1); editorRowDelchar(row, E.cursor_x - 1);
--E.cursor_x; E.cursor_x--;
} else { } else {
// At beginning of line - join with previous line
E.cursor_x = E.row[E.cursor_y - 1].size; E.cursor_x = E.row[E.cursor_y - 1].size;
editorRowAppendString(&E.row[E.cursor_y - 1], row->chars, row->size); editorRowAppendRow(&E.row[E.cursor_y - 1], row);
editorDelRow(E.cursor_y); editorDelRow(E.cursor_y);
--E.cursor_y; E.cursor_y--;
} }
} }
+126 -22
View File
@@ -1,13 +1,13 @@
#include "../include/file_io.h" #include "../include/file_io.h"
#include "../include/input.h" #include "../include/input.h"
#include "../include/output.h" #include "../include/output.h"
#include "data.h"
#include <fcntl.h> #include <fcntl.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h>
extern char *strdup(const char *); extern char *strdup(const char *);
extern ssize_t getline(char **restrict lineptr, size_t *restrict n, extern ssize_t getline(char **restrict lineptr, size_t *restrict n,
@@ -15,37 +15,56 @@ extern ssize_t getline(char **restrict lineptr, size_t *restrict n,
extern int ftruncate(int fd, off_t length); extern int ftruncate(int fd, off_t length);
extern struct editorConfig E; extern struct editorConfig E;
// Convert utf_8_char_t array to byte string
char *editorRowsToString(int *buffer_len) { char *editorRowsToString(int *buffer_len) {
int tot_len = 0; int tot_len = 0;
int j; int j, i;
char *buf; char *buf;
char *p; char *p;
// Calculate total byte length (not character count)
for (j = 0; j < E.numrows; ++j) { for (j = 0; j < E.numrows; ++j) {
tot_len += E.row[j].size + 1; // Count actual bytes in each character
for (i = 0; i < E.row[j].size; i++) {
tot_len += E.row[j].chars[i].len;
}
tot_len++; // For newline
} }
*buffer_len = tot_len; *buffer_len = tot_len;
buf = malloc(tot_len); buf = malloc(tot_len);
if (!buf) return NULL;
p = buf; p = buf;
for (j = 0; j < E.numrows; ++j) { for (j = 0; j < E.numrows; ++j) {
memcpy(p, E.row[j].chars, E.row[j].size); // Copy each character's bytes
p += E.row[j].size; for (i = 0; i < E.row[j].size; i++) {
*p = '\n'; for (int k = 0; k < E.row[j].chars[i].len; k++) {
p++; *p++ = E.row[j].chars[i].c[k];
}
}
*p++ = '\n';
} }
return buf; return buf;
} }
void editorCloseFile(void) { void editorCloseFile(void) {
// Free all rows
for (int i = 0; i < E.numrows; i++) {
editorFreeRow(&E.row[i]);
}
E.cursor_x = 0; E.cursor_x = 0;
E.cursor_y = 0; E.cursor_y = 0;
E.rx = 0; E.rx = 0;
E.row_offset = 0; E.row_offset = 0;
E.col_offset = 0; E.col_offset = 0;
E.numrows = 0; E.numrows = 0;
free(E.row);
E.row = NULL; E.row = NULL;
E.dirty = 0; E.dirty = 0;
free(E.filename);
E.filename = NULL; E.filename = NULL;
E.status_msg[0] = '\0'; E.status_msg[0] = '\0';
E.status_msg_time = 0; E.status_msg_time = 0;
@@ -57,25 +76,29 @@ void editorOpen(char *filename) {
// Test if a file is already open // Test if a file is already open
if (E.filename != NULL) { if (E.filename != NULL) {
editorCloseFile(); editorCloseFile();
E.state = READ_AND_WRITE;
} }
E.state = READ_AND_WRITE;
free(E.filename);
E.filename = strdup(filename); E.filename = strdup(filename);
fp = fopen(filename, "a+"); fp = fopen(filename, "r");
if (!fp) if (!fp) {
die("fopen"); // File doesn't exist - that's okay, we'll create it on save
E.dirty = 0;
return;
}
char *line = NULL; char *line = NULL;
size_t line_cap = 0; size_t line_cap = 0;
ssize_t line_len; ssize_t line_len;
while ((line_len = getline(&line, &line_cap, fp)) != -1) { while ((line_len = getline(&line, &line_cap, fp)) != -1) {
// Strip newline characters
while (line_len > 0 && while (line_len > 0 &&
(line[line_len - 1] == '\n' || line[line_len - 1] == '\r')) { (line[line_len - 1] == '\n' || line[line_len - 1] == '\r')) {
--line_len; --line_len;
} }
// editorInsertRow will convert bytes to utf_8_char_t
editorInsertRow(E.numrows, line, line_len); editorInsertRow(E.numrows, line, line_len);
} }
free(line); free(line);
@@ -87,27 +110,108 @@ void editorSave() {
int len; int len;
char *buf; char *buf;
int fd; int fd;
if (E.filename == NULL) { if (E.filename == NULL) {
E.filename = editorPrompt("Save as: %s (ESC to cancel)"); E.filename = editorPrompt("Save as: %s (ESC to cancel)", "", 1);
if (E.filename == NULL) { if (E.filename == NULL) {
editorSetStatusMessage("Save aborted"); editorSetStatusMessage("Save aborted");
return; return;
} }
} }
buf = editorRowsToString(&len); buf = editorRowsToString(&len);
fd = open(E.filename, O_RDWR | O_CREAT, 0644); if (!buf) {
editorSetStatusMessage("Can't save! Memory error");
return;
}
fd = open(E.filename, O_RDWR | O_CREAT | O_TRUNC, 0644);
if (fd != -1) { if (fd != -1) {
if (ftruncate(fd, len) != -1) { if (write(fd, buf, len) == len) {
if (write(fd, buf, len) == len) { close(fd);
close(fd); free(buf);
free(buf); E.dirty = 0;
E.dirty = 0; editorSetStatusMessage("%d bytes written to disk", len);
editorSetStatusMessage("%d bytes written to disk", len); return;
return;
}
} }
close(fd); close(fd);
} }
free(buf); free(buf);
editorSetStatusMessage("Can't save! I/O error: %s", strerror(errno)); editorSetStatusMessage("Can't save! I/O error: %s", strerror(errno));
} }
// Helper to convert utf_8_char_t array to byte string for searching
static char *row_to_string(erow *row) {
// Calculate byte length
int byte_len = 0;
for (int i = 0; i < row->rsize; i++) {
byte_len += row->render[i].len;
}
char *str = malloc(byte_len + 1);
if (!str) return NULL;
// Convert to bytes
int pos = 0;
for (int i = 0; i < row->rsize; i++) {
for (int j = 0; j < row->render[i].len; j++) {
str[pos++] = row->render[i].c[j];
}
}
str[pos] = '\0';
return str;
}
void editorFind() {
char *query = editorPrompt("Search: %s (ESC to cancel)", "", 0);
if (query == NULL) return;
int saved_cursor_x = E.cursor_x;
int saved_cursor_y = E.cursor_y;
int saved_row_offset = E.row_offset;
int saved_col_offset = E.col_offset;
// Search from current position forward
for (int i = E.cursor_y; i < E.numrows; i++) {
erow *row = &E.row[i];
// Convert row to byte string for searching
char *render_str = row_to_string(row);
if (!render_str) continue;
char *match = strstr(render_str, query);
if (match) {
E.cursor_y = i;
// Find the character index from byte position
int byte_pos = match - render_str;
int char_idx = 0;
int current_byte = 0;
for (char_idx = 0; char_idx < row->rsize; char_idx++) {
if (current_byte >= byte_pos) break;
current_byte += row->render[char_idx].len;
}
E.cursor_x = editorRowRxToCx(row, char_idx);
E.row_offset = E.numrows; // Force scroll
free(render_str);
free(query);
return;
}
free(render_str);
}
// Not found - restore cursor position
E.cursor_x = saved_cursor_x;
E.cursor_y = saved_cursor_y;
E.row_offset = saved_row_offset;
E.col_offset = saved_col_offset;
editorSetStatusMessage("Not found: %s", query);
free(query);
}
+13 -5
View File
@@ -5,8 +5,8 @@
#include <stdio.h> #include <stdio.h>
#define LISP_IMPLEMENTATION #define LISP_IMPLEMENTATION
#include "../lisp-interpreter/dist/lisp.h" #include "../include/lisp.h"
#include "../lisp-interpreter/dist/lisp_lib.h" #include "../include/lisp_lib.h"
extern struct editorConfig; extern struct editorConfig;
@@ -31,9 +31,14 @@ void initBuiltins() {
registerBuiltin("MOVE-CURSOR-PAGE-DOWN", editorMoveCursorPageDown); registerBuiltin("MOVE-CURSOR-PAGE-DOWN", editorMoveCursorPageDown);
registerBuiltin("EDITOR-OPEN-FILE", editorOpenFile); registerBuiltin("EDITOR-OPEN-FILE", editorOpenFile);
registerBuiltin("EDITOR-INSERT-CHAR", editorPrintC); registerBuiltin("EDITOR-INSERT-CHAR", editorPrintC);
registerBuiltin("ADD-PACKAGE", addPackage);
registerBuiltin("EDITOR-DEL-ROW", editorDelRow_L);
registerBuiltin("EDITOR-FIND", editorFind_L);
registerBuiltin("EDITOR-READ-CHAR", editorReadChar_L);
} }
void initEditor() { void initEditor() {
char * init_file_path = (char *) calloc(256, sizeof(char));
E.cursor_x = 0; E.cursor_x = 0;
E.cursor_y = 0; E.cursor_y = 0;
E.rx = 0; E.rx = 0;
@@ -53,8 +58,10 @@ void initEditor() {
E.number_of_keybinds = 0; E.number_of_keybinds = 0;
strcat(init_file_path, getenv("HOME"));
E.fd_init_file = fopen("config/init.lisp", "r"); strcat(init_file_path, "/.beluga/config/init.lisp");
printf("%s\n", init_file_path);
E.fd_init_file = fopen(init_file_path, "r");
E.ctx = lisp_init(); E.ctx = lisp_init();
E.env = lisp_env(E.ctx); E.env = lisp_env(E.ctx);
lisp_lib_load(E.ctx); lisp_lib_load(E.ctx);
@@ -63,6 +70,7 @@ void initEditor() {
// Read config file // Read config file
E.ctx_data = lisp_read_file(E.fd_init_file, &E.ctx_error, E.ctx); E.ctx_data = lisp_read_file(E.fd_init_file, &E.ctx_error, E.ctx);
free(init_file_path);
if (E.ctx_error != LISP_ERROR_NONE) { if (E.ctx_error != LISP_ERROR_NONE) {
die("init failed"); die("init failed");
} }
@@ -78,6 +86,6 @@ void initEditor() {
(int)lisp_eval(lisp_read("QUIT-TIMES", &E.ctx_error, E.ctx), &E.ctx_error, (int)lisp_eval(lisp_read("QUIT-TIMES", &E.ctx_error, E.ctx), &E.ctx_error,
E.ctx) E.ctx)
.val.int_val; .val.int_val;
fprintf(stderr, "Tab %d\n", E.constantes.QUIT_TIMES);
E.quit_times_buffer = E.constantes.QUIT_TIMES; E.quit_times_buffer = E.constantes.QUIT_TIMES;
} }
+160 -95
View File
@@ -1,127 +1,147 @@
#include "../include/input.h" #include "../include/input.h"
#include "../include/define.h"
#include "../include/editor_op.h" #include "../include/editor_op.h"
#include "../include/output.h" #include "../include/output.h"
#include "../include/define.h" #include "include/data.h"
#include <ctype.h> #include <ctype.h>
#include <dirent.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
extern struct editorConfig E; extern struct editorConfig E;
char *file_completion(const char *path) {
DIR *dir;
struct dirent *entry;
char directory[128];
char predict[128];
int predict_len = 0;
if (path[strlen(path) - 1] == '/') {
return strdup(path);
}
// Find dir name
char *last_slash = strrchr(path, '/');
if (last_slash) {
size_t dir_len = last_slash - path + 1;
strncpy(directory, path, dir_len);
predict_len = strlen(path) - dir_len;
strncpy(predict, last_slash + 1, predict_len);
directory[dir_len] = '\0';
predict[predict_len] = '\0';
} else {
return NULL;
}
dir = opendir(directory);
if (!dir)
return NULL;
while ((entry = readdir(dir)) != NULL) {
if (strncmp(entry->d_name, predict, predict_len) == 0) {
static char full_path[128];
snprintf(full_path, sizeof(full_path), "%s%s", directory, entry->d_name);
struct stat st;
if (stat(full_path, &st) == 0 && S_ISDIR(st.st_mode)) {
strcat(full_path, "/");
}
closedir(dir);
return strdup(full_path);
}
}
closedir(dir);
return NULL;
}
/** /**
* \fn char * editorPrompt(struct editorConfig *E, char *prompt) * \fn char * editorPrompt(struct editorConfig *E, char *prompt, char bPathMode)
* \brief Return user input in a prompt when enter is hit. */ * \brief Return user input in a prompt when enter is hit. */
char *editorPrompt(char *prompt) { char *editorPrompt(char *prompt, char *placeHolder, char bPathMode) {
size_t buf_size = 128; size_t buf_size = 128;
char *buf = malloc(buf_size); char *buf = malloc(buf_size);
size_t buf_len = 0; size_t buf_len = 0;
int c = 0;
buf[0] = '\0'; buf[0] = '\0';
strcpy(buf, placeHolder);
buf_len = strlen(placeHolder);
while (1) { while (1) {
editorSetStatusMessage(prompt, buf); editorSetStatusMessage(prompt, buf);
editorRefreshScreen(); editorRefreshScreen();
c = editorReadKey();
if (c == DEL_KEY || c == CTRL_KEY('h') || c == BACKSPACE) { KeyInfo *key = editorReadKey();
// Handle backspace/delete
if (key->type == KEY_SPECIAL && (key->data.special == 127 || key->data.special == 8)) {
if (buf_len != 0) { if (buf_len != 0) {
buf[--buf_len] = '\0'; buf[--buf_len] = '\0';
} }
} else if (c == ESCAPE) { }
// Handle Ctrl+H (backspace)
else if (key->type == KEY_CTRL && key->data.ctrl_char == 'H') {
if (buf_len != 0) {
buf[--buf_len] = '\0';
}
}
// Handle ESC
else if (key->type == KEY_SPECIAL && key->data.special == 27) {
editorSetStatusMessage(""); editorSetStatusMessage("");
free(buf); free(buf);
return NULL; return NULL;
} else if (c == '\r') { }
// Handle Enter
else if (key->type == KEY_SPECIAL && (key->data.special == 13 || key->data.special == 10)) {
if (buf_len != 0) { if (buf_len != 0) {
editorSetStatusMessage(""); editorSetStatusMessage("");
return buf; return buf;
} }
} else if (!iscntrl(c) && c < 128) { }
// Handle Tab for path completion
else if (bPathMode && key->type == KEY_SPECIAL && key->data.special == 9) {
char path[128];
char *pwd;
if (buf[0] != '/') {
pwd = getenv("PWD");
snprintf(path, sizeof(path), "%s/%s", pwd, buf);
} else {
strcpy(path, buf);
}
char *completion = file_completion(path);
if (completion) {
memset(buf, 0, buf_size);
strcpy(buf, completion);
buf_len = strlen(buf);
free(completion);
}
}
// Handle regular characters (ASCII only for prompts)
else if (key->type == KEY_CHAR && key->data.codepoint < 128) {
if (buf_len == buf_size - 1) { if (buf_len == buf_size - 1) {
buf_size *= 2; buf_size *= 2;
buf = realloc(buf, buf_size); buf = realloc(buf, buf_size);
} }
buf[buf_len++] = c; buf[buf_len++] = (char)key->data.codepoint;
buf[buf_len] = '\0'; buf[buf_len] = '\0';
} }
} }
} }
char *key_to_string(int key) { void editorMoveCursor(KeyInfo *key) {
static char key_str[32]; if (key->type != KEY_ARROW) return;
char tmp[10];
sprintf(tmp, "%d", key);
// First test enter key
if (key == '\r') {
strcpy(key_str, "ENTER");
} else if (key >= 1 && key <= 26) { // CTRL keys
snprintf(key_str, sizeof(key_str), "CTRL-%c", 'a' + key - 1);
} else {
switch (key) {
case ARROW_UP:
strcpy(key_str, "ARROW-UP");
break;
case ARROW_DOWN:
strcpy(key_str, "ARROW-DOWN");
break;
case ARROW_LEFT:
strcpy(key_str, "ARROW-LEFT");
break;
case ARROW_RIGHT:
strcpy(key_str, "ARROW-RIGHT");
break;
case PAGE_UP:
strcpy(key_str, "PAGE-UP");
fprintf(stderr, "pagr up\n");
break;
case PAGE_DOWN:
strcpy(key_str, "PAGE-DOWN");
break;
case DEL_KEY:
fprintf(stderr, "delete key\n");
strcpy(key_str, "DEL");
break;
case BACKSPACE:
strcpy(key_str, "BACKSPACE");
break;
case '\r':
strcpy(key_str, "ENTER");
break;
case '\x1b':
strcpy(key_str, "ESCAPE");
break;
case BEG_LINE:
strcpy(key_str, "HOME");
break;
case END_LINE:
strcpy(key_str, "END");
break;
default:
// For regular characters
if (isprint(key)) {
snprintf(key_str, sizeof(key_str), "%c", key);
} else {
snprintf(key_str, sizeof(key_str), "KEY-%d", key);
}
}
}
return key_str;
}
void editorMoveCursor(int key) {
erow *row = (E.cursor_y >= E.numrows) ? NULL : &E.row[E.cursor_y]; erow *row = (E.cursor_y >= E.numrows) ? NULL : &E.row[E.cursor_y];
int row_len; int row_len;
switch (key) {
case ARROW_RIGHT: switch (key->data.arrow) {
case 'C': // Right
if (row && E.cursor_x < row->size) { if (row && E.cursor_x < row->size) {
++E.cursor_x; ++E.cursor_x;
} else if (row && E.cursor_x == row->size) { } else if (row && E.cursor_x == row->size) {
@@ -129,17 +149,17 @@ void editorMoveCursor(int key) {
E.cursor_x = 0; E.cursor_x = 0;
} }
break; break;
case ARROW_DOWN: case 'B': // Down
if (E.cursor_y < E.numrows) { if (E.cursor_y < E.numrows) {
++E.cursor_y; ++E.cursor_y;
} }
break; break;
case ARROW_UP: case 'A': // Up
if (E.cursor_y != 0) { if (E.cursor_y != 0) {
--E.cursor_y; --E.cursor_y;
} }
break; break;
case ARROW_LEFT: case 'D': // Left
if (E.cursor_x != 0) { if (E.cursor_x != 0) {
--E.cursor_x; --E.cursor_x;
} else if (E.cursor_y > 0) { } else if (E.cursor_y > 0) {
@@ -156,28 +176,73 @@ void editorMoveCursor(int key) {
} }
} }
int executeKeyBind(char *key_sequence) { KeyInfo *stringToCodepoint(const char *string) {
int i; KeyInfo *key = (KeyInfo *)malloc(sizeof(KeyInfo));
for (i = 0; i < E.number_of_keybinds; ++i) { // test control key
if (!strcmp(key_sequence, E.key_binds[i].key_sequence)) { if (!strncmp("CTRL", string, 4)) {
key->type = KEY_CTRL;
key->data.ctrl_char = toupper(string[6]) + 64;
} else if (!strncmp("ARROW", string, 5)) {
key->type = KEY_ARROW;
if (!strcmp("UP", string + 7)) {
key->data.arrow = 'A';
} else if (!strcmp("DOWN", string + 7)) {
key->data.arrow = 'B';
} else if (!strcmp("RIGHT", string + 7)) {
key->data.arrow = 'C';
} else if (!strcmp("LEFT", string + 7)) {
key->data.arrow = 'D';
}
}
fprintf(stderr, "lisp function %s\n", key_sequence); return key;
// It's a symbol, create a function call }
lisp_eval(lisp_cons(E.key_binds[i].command, lisp_null(), E.ctx),
&E.ctx_error, E.ctx); static int key_match(KeyInfo *a, KeyInfo *b) {
return 1; if (a->type != b->type) return 0;
if (a->modifiers != b->modifiers) return 0;
switch (a->type) {
case KEY_CTRL:
return toupper(a->data.ctrl_char) == toupper(b->data.ctrl_char);
case KEY_ALT:
return a->data.alt_char == b->data.alt_char;
case KEY_ARROW:
return a->data.arrow == b->data.arrow;
case KEY_FUNCTION:
return a->data.function_num == b->data.function_num;
case KEY_CHAR:
return a->data.codepoint == b->data.codepoint;
case KEY_SPECIAL:
case KEY_NAVIGATION:
return a->data.special == b->data.special;
default:
return 0;
}
}
int executeKeyBind(KeyInfo *key_sequence) {
for (int i = 0; i < E.number_of_keybinds; ++i) {
fprintf(stderr, "Keybind found\n");
if (key_match(key_sequence, E.key_binds[i].key_sequence)) {
// Execute the lisp command
lisp_eval(lisp_cons(E.key_binds[i].command, lisp_null(), E.ctx),
&E.ctx_error, E.ctx);
return 1;
} }
} }
return 0; return 0;
} }
void editorProcessKeypress() { void editorProcessKeypress() {
int c = editorReadKey(); KeyInfo *key = editorReadKey();
if (!key)
return;
if (executeKeyBind(key_to_string(c))) { if (executeKeyBind(key)) {
fprintf(stderr, "Keybinds found\n");
return; return;
} }
editorInsertChar(c); editorInsertChar(&key->c);
E.quit_times_buffer = E.constantes.QUIT_TIMES; E.quit_times_buffer = E.constantes.QUIT_TIMES;
} }
+21 -1
View File
@@ -6,6 +6,17 @@
extern struct editorConfig E; extern struct editorConfig E;
static void utf8_to_bytes(utf_8_char_t *chars, int count, unsigned char *output, int *output_len) {
int pos = 0;
for (int i = 0; i < count; i++) {
for (int j = 0; j < chars[i].len; j++) {
output[pos++] = chars[i].c[j];
}
fprintf(stderr, "bytes length : %s %d\n", chars[i].c, pos);
}
*output_len = pos;
}
void editorDrawRows(struct abuf *ab) { void editorDrawRows(struct abuf *ab) {
int y; int y;
char welcome[80]; char welcome[80];
@@ -41,7 +52,16 @@ void editorDrawRows(struct abuf *ab) {
len = 0; len = 0;
if (len > E.screencols) if (len > E.screencols)
len = E.screencols; len = E.screencols;
abAppend(ab, &E.row[file_row].render[E.col_offset], len); if (len > 0) {
unsigned char *display_buf = malloc(len * 4); // Max 4 bytes per char
int byte_len;
utf8_to_bytes(&E.row[file_row].render[E.col_offset], len, display_buf,
&byte_len);
abAppend(ab, display_buf, byte_len);
fprintf(stderr, "display buffer : %s %d\n", display_buf, byte_len);
free(display_buf);
}
} }
abAppend(ab, ERASE_END_LINE, 3); abAppend(ab, ERASE_END_LINE, 3);
abAppend(ab, "\r\n", 2); abAppend(ab, "\r\n", 2);
+117 -25
View File
@@ -1,5 +1,6 @@
#include "../include/row_op.h" #include "../include/row_op.h"
#include "data.h" #include "include/data.h"
#include "include/define.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@@ -7,11 +8,29 @@
extern struct editorConfig E; extern struct editorConfig E;
static int is_tab(utf_8_char_t *ch) {
return ch->len == 1 && ch->c[0] == '\t';
}
// Helper function to check if two utf_8_char_t are equal
static int utf8_char_equal(utf_8_char_t *a, utf_8_char_t *b) {
if (a->len != b->len) return 0;
return memcmp(a->c, b->c, a->len) == 0;
}
// Helper function to create a space character
static utf_8_char_t make_space() {
utf_8_char_t space;
space.c[0] = ' ';
space.len = 1;
return space;
}
int editorRowCxToRx(erow *row, int cursor_x) { int editorRowCxToRx(erow *row, int cursor_x) {
int render_x = 0; int render_x = 0;
int i; int i;
for (i = 0; i < cursor_x; ++i) { for (i = 0; i < cursor_x; ++i) {
if (row->chars[i] == '\t') { if (is_tab(&row->chars[i])) {
render_x += (E.constantes.TAB_LENGTH - 1) - (render_x % E.constantes.TAB_LENGTH); render_x += (E.constantes.TAB_LENGTH - 1) - (render_x % E.constantes.TAB_LENGTH);
} }
render_x++; render_x++;
@@ -19,6 +38,18 @@ int editorRowCxToRx(erow *row, int cursor_x) {
return render_x; return render_x;
} }
int editorRowRxToCx(erow *row, int rx) {
int cur_rx = 0;
int cx;
for (cx = 0; cx < row->size; cx++) {
if (is_tab(&row->chars[cx]))
cur_rx += (E.constantes.TAB_LENGTH - 1) - (cur_rx % E.constantes.TAB_LENGTH);
cur_rx++;
if (cur_rx > rx) return cx;
}
return cx;
}
/** /**
* \fn editorUpdateRow(erow *row) * \fn editorUpdateRow(erow *row)
* \brief Copy content of \p row in \p row->render. * \brief Copy content of \p row in \p row->render.
@@ -28,40 +59,42 @@ void editorUpdateRow(erow *row) {
int i, i_render; int i, i_render;
int tabs = 0; int tabs = 0;
// counting number of tabs // Count number of tabs
for (i = 0; i < row->size; ++i) { for (i = 0; i < row->size; ++i) {
tabs += if (is_tab(&row->chars[i])) {
(row->chars[i] == '\t'); /**< increment tabs of 1 if chars[i] is one. */ tabs++;
}
} }
free(row->render); free(row->render);
row->render = malloc(row->size + tabs * (E.constantes.TAB_LENGTH - 1) + // Allocate space for utf_8_char_t array
1); /**< Tabs needs E.constantes.TAB_LENGTH chars so E.constantes.TAB_LENGTH - 1 row->render = malloc(sizeof(utf_8_char_t) * (row->size + tabs * (E.constantes.TAB_LENGTH - 1)));
more than the first already counted. */
if (!row->render) {
row->rsize = 0;
return;
}
// end of counting
i_render = 0; i_render = 0;
for (i = 0; i < row->size; ++i) { for (i = 0; i < row->size; ++i) {
if (row->chars[i] == '\t') { if (is_tab(&row->chars[i])) {
row->render[i_render++] = ' '; // Replace tab with spaces
row->render[i_render++] = make_space();
while (i_render % E.constantes.TAB_LENGTH) { while (i_render % E.constantes.TAB_LENGTH) {
row->render[i_render++] = row->render[i_render++] = make_space();
' '; /**< Addind the right amount of spaces for tabs */
} }
} else { } else {
row->render[i_render++] = row->chars[i]; row->render[i_render++] = row->chars[i];
} }
} }
row->render[i_render] = '\0'; // Don't forget the end of string character.
row->rsize = i_render; row->rsize = i_render;
} }
void editorInsertRow(int at, char *s, size_t len) { void editorInsertRow(int at, char *s, size_t len) {
if (at < 0 || at > E.numrows) { if (at < 0 || at > E.numrows) {
return; return;
} }
erow *tmp = (erow *)realloc(E.row, sizeof(erow) * (E.numrows + 1)); erow *tmp = (erow *)realloc(E.row, sizeof(erow) * (E.numrows + 1));
if (!tmp) { if (!tmp) {
return; return;
@@ -69,19 +102,78 @@ void editorInsertRow(int at, char *s, size_t len) {
E.row = tmp; E.row = tmp;
memmove(&E.row[at + 1], &E.row[at], sizeof(erow) * (E.numrows - at)); memmove(&E.row[at + 1], &E.row[at], sizeof(erow) * (E.numrows - at));
E.row[at].size = len; // Initialize the new row
E.row[at].chars = malloc(len + 1); E.row[at].size = 0;
memcpy(E.row[at].chars, s, len); E.row[at].chars = NULL;
E.row[at].chars[len] = '\0';
E.row[at].rsize = 0; E.row[at].rsize = 0;
E.row[at].render = NULL; E.row[at].render = NULL;
// Count UTF-8 characters first
int char_count = 0;
int i = 0;
while (i < len) {
unsigned char first = (unsigned char)s[i];
int char_len;
if ((first & 0x80) == 0) {
char_len = 1;
} else if ((first & 0xE0) == 0xC0) {
char_len = 2;
} else if ((first & 0xF0) == 0xE0) {
char_len = 3;
} else if ((first & 0xF8) == 0xF0) {
char_len = 4;
} else {
char_len = 1; // Invalid, treat as single byte
}
i += char_len;
char_count++;
}
// Allocate for the actual number of characters
if (char_count > 0) {
E.row[at].chars = malloc(sizeof(utf_8_char_t) * char_count);
if (!E.row[at].chars) {
return;
}
}
// Now convert to utf_8_char_t array
i = 0;
E.row[at].size = 0;
while (i < len && E.row[at].size < char_count) {
utf_8_char_t ch;
unsigned char first = (unsigned char)s[i];
if ((first & 0x80) == 0) {
ch.len = 1;
} else if ((first & 0xE0) == 0xC0) {
ch.len = 2;
} else if ((first & 0xF0) == 0xE0) {
ch.len = 3;
} else if ((first & 0xF8) == 0xF0) {
ch.len = 4;
} else {
ch.len = 1;
}
// Copy bytes
for (int j = 0; j < ch.len && i < len; j++) {
ch.c[j] = s[i++];
}
E.row[at].chars[E.row[at].size++] = ch;
}
editorUpdateRow(&E.row[at]); editorUpdateRow(&E.row[at]);
++E.numrows; ++E.numrows;
++E.dirty; ++E.dirty;
} }
void editorFreeRow(erow *row) { void editorFreeRow(erow *row) {
free(row->render); free(row->render);
free(row->chars); free(row->chars);
@@ -101,16 +193,17 @@ void editorDelRow(int at) {
* \fn editorRowInsertChar(erow *row, int at, int c) * \fn editorRowInsertChar(erow *row, int at, int c)
* \param at Index of where we want to insert the char */ * \param at Index of where we want to insert the char */
void editorRowInsertChar(erow *row, int at, int c) { void editorRowInsertChar(erow *row, int at, utf_8_char_t c) {
if (E.state == READ_ONLY) if (E.state == READ_ONLY)
return; return;
if (at < 0 || at > row->size) { if (at < 0 || at > row->size) {
at = row->size; at = row->size;
} }
row->chars = realloc(row->chars, row->size + 2); row->chars = realloc(row->chars, row->size + 1);
memmove(&row->chars[at + 1], &row->chars[at], row->size - at + 1); memmove(&row->chars[at + 1], &row->chars[at], row->size - at + 1);
++row->size; ++(row->size);
row->chars[at] = c; row->chars[at] = c;
fprintf(stderr, "Row insert : %s %d\n", c.c, c.len);
editorUpdateRow(row); editorUpdateRow(row);
++E.dirty; ++E.dirty;
} }
@@ -119,7 +212,6 @@ void editorRowAppendString(erow *row, char *s, size_t len) {
row->chars = realloc(row->chars, row->size + len + 1); row->chars = realloc(row->chars, row->size + len + 1);
memcpy(&row->chars[row->size], s, len); memcpy(&row->chars[row->size], s, len);
row->size += len; row->size += len;
row->chars[row->size] = '\0';
editorUpdateRow(row); editorUpdateRow(row);
++E.dirty; ++E.dirty;
} }
+197 -57
View File
@@ -2,6 +2,8 @@
#include "../include/data.h" #include "../include/data.h"
#include <stdio.h> #include <stdio.h>
#include <unistd.h>
#include <string.h>
void die(const char *s) { void die(const char *s) {
write(STDOUT_FILENO, "\x1b[2J", 4); write(STDOUT_FILENO, "\x1b[2J", 4);
@@ -35,73 +37,211 @@ void enableRawMode() {
} }
} }
int editorReadKey() { int utf8_char_length(unsigned char first_byte) {
int nread; if ((first_byte & 0x80) == 0)
char c; return 1; // 0xxxxxxx - ASCII
char seq[3]; if ((first_byte & 0xE0) == 0xC0)
while ((nread = read(STDIN_FILENO, &c, 1)) != 1) { return 2; // 110xxxxx - 2 bytes
if (nread == -1 && errno != EAGAIN) { if ((first_byte & 0xF0) == 0xE0)
die("read"); return 3; // 1110xxxx - 3 bytes
if ((first_byte & 0xF8) == 0xF0)
return 4; // 11110xxx - 4 bytes
return 1; // Invalid, treat as single byte
}
// Convert UTF-8 to Unicode code point
unsigned int utf8_to_codepoint(const unsigned char *bytes, int len) {
if (len == 1)
return bytes[0];
if (len == 2)
return ((bytes[0] & 0x1F) << 6) | (bytes[1] & 0x3F);
if (len == 3)
return ((bytes[0] & 0x0F) << 12) | ((bytes[1] & 0x3F) << 6) |
(bytes[2] & 0x3F);
if (len == 4)
return ((bytes[0] & 0x07) << 18) | ((bytes[1] & 0x3F) << 12) |
((bytes[2] & 0x3F) << 6) | (bytes[3] & 0x3F);
return 0;
}
void parse_key(unsigned char *seq, int len, KeyInfo *key) {
memcpy(key->c.c, seq, len);
key->c.len = len;
key->modifiers = MOD_NONE;
key->type = KEY_UNKNOWN;
// Control characters (Ctrl+A to Ctrl+Z)
if (len == 1 && seq[0] < 32 && seq[0] != 27 && seq[0] != 9 && seq[0] != 10 &&
seq[0] != 13) {
key->type = KEY_CTRL;
key->data.ctrl_char = seq[0] + 64;
return;
}
// Special single characters
if (len == 1) {
switch (seq[0]) {
case 9:
case 10:
case 13:
case 27:
case 127:
key->type = KEY_SPECIAL;
key->data.special = seq[0];
return;
} }
} }
if (c == '\x1b') { // Escape sequences
if (read(STDIN_FILENO, &seq[0], 1) != 1 || if (len >= 2 && seq[0] == 27) {
read(STDIN_FILENO, &seq[1], 1) != 1) { // Alt+key combinations
return '\x1b'; if (len == 2 && seq[1] >= 32 && seq[1] < 127) {
key->type = KEY_ALT;
key->data.alt_char = seq[1];
return;
} }
if (seq[0] == '[') {
if (seq[1] >= '0' && seq[1] <= '9') { // CSI sequences (ESC [ ...)
if (read(STDIN_FILENO, &seq[2], 1) != 1) { if (len >= 3 && seq[1] == '[') {
return '\x1b'; // Arrow keys
if (len == 3) {
switch (seq[2]) {
case 'A':
case 'B':
case 'C':
case 'D':
key->type = KEY_ARROW;
key->data.arrow = seq[2];
return;
case 'H':
case 'F':
key->type = KEY_NAVIGATION;
key->data.special = seq[2];
return;
} }
if (seq[2] == '~') { }
switch (seq[1]) {
case '1': // Modified keys (ESC [ 1 ; modifier letter)
return BEG_LINE; if (len >= 6 && seq[2] == '1' && seq[3] == ';') {
case '3': int modifier = seq[4] - '0';
return DEL_KEY; char k = seq[5];
case '4':
return END_LINE; if (modifier & 1)
case '5': key->modifiers |= MOD_SHIFT;
return PAGE_UP; if (modifier & 2)
case '6': key->modifiers |= MOD_ALT;
return PAGE_DOWN; if (modifier & 4)
case '7': key->modifiers |= MOD_CTRL;
return BEG_LINE;
case '8': switch (k) {
return END_LINE; case 'A':
case 'B':
case 'C':
case 'D':
key->type = KEY_ARROW;
key->data.arrow = k;
return;
case 'H':
case 'F':
key->type = KEY_NAVIGATION;
key->data.special = k;
return;
}
}
// Function keys and navigation
if (len == 4 && seq[3] == '~') {
int num = seq[2] - '0';
if (num >= 1 && num <= 6) {
key->type = KEY_NAVIGATION;
key->data.special = seq[2];
return;
}
}
if (len == 5 && seq[4] == '~') {
int num = (seq[2] - '0') * 10 + (seq[3] - '0');
if (num >= 15 && num <= 24) {
key->type = KEY_FUNCTION;
// Map to F5-F12
int f_map[] = {15, 17, 18, 19, 20, 21, 23, 24};
for (int i = 0; i < 8; i++) {
if (f_map[i] == num) {
key->data.function_num = i + 5;
return;
}
} }
} }
} else {
switch (seq[1]) {
case 'A':
return ARROW_UP;
case 'B':
return ARROW_DOWN;
case 'C':
return ARROW_RIGHT;
case 'D':
return ARROW_LEFT;
case 'H':
return BEG_LINE;
case 'F':
return END_LINE;
}
}
} else if (seq[0] == 'O') {
switch (seq[1]) {
case 'H':
return BEG_LINE;
case 'F':
return END_LINE;
} }
} }
return '\x1b';
} else { // SS3 sequences (ESC O ...)
return c; if (len == 3 && seq[1] == 'O') {
switch (seq[2]) {
case 'P':
case 'Q':
case 'R':
case 'S':
key->type = KEY_FUNCTION;
key->data.function_num = seq[2] - 'P' + 1;
return;
case 'H':
case 'F':
key->type = KEY_NAVIGATION;
key->data.special = seq[2];
return;
}
}
} }
// UTF-8 character
if (seq[0] >= 32 || (seq[0] & 0x80)) {
int char_len = utf8_char_length(seq[0]);
fprintf(stderr, "char length : %d\n", char_len);
if (char_len <= len) {
key->type = KEY_CHAR;
memcpy(key->c.c, seq, len);
key->c.len = len;
return;
}
}
}
KeyInfo *editorReadKey() {
fd_set fds;
int timeout_ms = 10;
struct timeval tv;
int total = 0;
KeyInfo *key = (KeyInfo *)malloc(sizeof(KeyInfo));
int len;
unsigned char buffer[20];
if (read(STDIN_FILENO, &buffer[0], 1) <= 0)
return 0;
while (total < 20) {
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);
tv.tv_sec = 0;
tv.tv_usec = timeout_ms * 1000;
int ret = select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv);
if (ret <= 0)
break;
if (read(STDIN_FILENO, &buffer[total], 1) <= 0)
break;
total++;
}
total++;
parse_key(buffer, total, key);
// DEBUG
fprintf(stderr, "%s %d %d %s %d\n", buffer, buffer[0], buffer[1], key->c.c, key->c.len);
return key;
} }
int getCursorPosition(int *rows, int *cols) { int getCursorPosition(int *rows, int *cols) {