mirror of
https://github.com/janet-lang/janet
synced 2026-04-07 23:41:27 +00:00
Compare commits
113 Commits
keyword-oo
...
core-image
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b61d1a0a0e | ||
|
|
89ef4eb634 | ||
|
|
114a45306d | ||
|
|
fe27df528c | ||
|
|
6321c30cb1 | ||
|
|
8343c9edd1 | ||
|
|
74e1a3273f | ||
|
|
1394dbbd57 | ||
|
|
f6a3853131 | ||
|
|
49465f71f3 | ||
|
|
960cf76eb5 | ||
|
|
1b735564fa | ||
|
|
7ae01d25dd | ||
|
|
cb5263d2d8 | ||
|
|
602092f6d5 | ||
|
|
d3a067a665 | ||
|
|
98a26f5ce3 | ||
|
|
09d9dca5f5 | ||
|
|
8a3f512746 | ||
|
|
19e59705b9 | ||
|
|
367c9da856 | ||
|
|
4bcf6565cd | ||
|
|
0c950d0846 | ||
|
|
7ba925c50a | ||
|
|
cb3b9dd76f | ||
|
|
f4fa55027b | ||
|
|
0fe11adb9c | ||
|
|
b138ee6e8e | ||
|
|
a66f19f636 | ||
|
|
c76f4e89d8 | ||
|
|
85a211b26b | ||
|
|
fe3620529f | ||
|
|
a7551e9b4e | ||
|
|
46c540b93e | ||
|
|
32c209ede9 | ||
|
|
0d293cd3f5 | ||
|
|
f284776490 | ||
|
|
38a7e4faf1 | ||
|
|
c333cbfa55 | ||
|
|
f72aa64f41 | ||
|
|
d85892edc8 | ||
|
|
56383b2ecc | ||
|
|
0d729eaab1 | ||
|
|
17ab654ccb | ||
|
|
872d03ae1d | ||
|
|
ee5fa54134 | ||
|
|
68e00cdb7a | ||
|
|
5bf9e4fc89 | ||
|
|
7350bf5dd9 | ||
|
|
e755f98300 | ||
|
|
8ee2f0a1d6 | ||
|
|
0726de34ff | ||
|
|
00301ad26b | ||
|
|
611543c48b | ||
|
|
4d81fbc238 | ||
|
|
c5012ca4c1 | ||
|
|
e68a889fa9 | ||
|
|
795e7a9de8 | ||
|
|
090a6a8c5c | ||
|
|
2bbf9fdcc5 | ||
|
|
0025f6ac87 | ||
|
|
737b2449f0 | ||
|
|
f7a0133eb1 | ||
|
|
48b179d67e | ||
|
|
d1a075b2a6 | ||
|
|
2bad24371d | ||
|
|
bf8d5da3dc | ||
|
|
4a6fcb5e23 | ||
|
|
5ba969f91d | ||
|
|
26818a5e5c | ||
|
|
b84b0e4828 | ||
|
|
b4934ceddc | ||
|
|
c4114fbcdb | ||
|
|
95f2bbe0a0 | ||
|
|
63137b8107 | ||
|
|
2c1b506213 | ||
|
|
612a245961 | ||
|
|
4b8edef58c | ||
|
|
82cddef5bb | ||
|
|
d0fc29338c | ||
|
|
4eeadd7463 | ||
|
|
f0fcdf6bc5 | ||
|
|
2a333f8359 | ||
|
|
0dd867d508 | ||
|
|
e3f902cb8a | ||
|
|
c651b6f67c | ||
|
|
3a9b50ea4a | ||
|
|
1304f9263b | ||
|
|
90313afd40 | ||
|
|
99f176f37b | ||
|
|
d0ec89c7c1 | ||
|
|
170e785b72 | ||
|
|
e53778d5d8 | ||
|
|
192705113e | ||
|
|
97a42ea17b | ||
|
|
2cd489b9d4 | ||
|
|
ff0d3a0081 | ||
|
|
282c02c475 | ||
|
|
798c88b4c8 | ||
|
|
83f4a11bf3 | ||
|
|
d7626f8c57 | ||
|
|
1efca2ebe7 | ||
|
|
40845b5c1b | ||
|
|
84fb07dd5a | ||
|
|
62cb3f81fe | ||
|
|
16ebb11181 | ||
|
|
115ed9cbb9 | ||
|
|
3ae6f64de5 | ||
|
|
ff3f7487a4 | ||
|
|
f0afb3c311 | ||
|
|
5b1a3b8208 | ||
|
|
b1e0849a2f | ||
|
|
67f26b7d72 |
@@ -3,7 +3,7 @@ packages:
|
||||
- gmake
|
||||
- gcc
|
||||
sources:
|
||||
- https://github.com/bakpakin/janet.git
|
||||
- https://github.com/janet-lang/janet.git
|
||||
tasks:
|
||||
- build: |
|
||||
cd janet
|
||||
|
||||
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# Use an approximate language for syntax highlighting (clojure is pretty close)
|
||||
*.janet linguist-language=clojure
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -12,6 +12,9 @@ janet
|
||||
janet-*.tar.gz
|
||||
dist
|
||||
|
||||
# Local directory for testing
|
||||
local
|
||||
|
||||
# Emscripten
|
||||
*.bc
|
||||
janet.js
|
||||
|
||||
@@ -19,5 +19,5 @@ deploy:
|
||||
skip_cleanup: true
|
||||
on:
|
||||
tags: true
|
||||
repo: bakpakin/janet
|
||||
repo: janet-lang/janet
|
||||
condition: "$CC = clang"
|
||||
|
||||
23
CHANGELOG.md
Normal file
23
CHANGELOG.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## 0.4.0 - ??
|
||||
- Add methods to parser values that mirror the api.
|
||||
- Add janet\_getmethod to CAPI for easier use of method like syntax.
|
||||
- Add get/set to abstract types to allow them to behave more
|
||||
like objects with methods.
|
||||
- Add parser/insert to modify parser state programmatically
|
||||
- Add debug/stacktrace for easy, pretty stacktraces
|
||||
- Remove the status-pp function
|
||||
- Update API to run-context to be much more sane
|
||||
- Add :lflags option to cook/make-native
|
||||
- Disallow NaNs as table or struct keys
|
||||
- Update module resolution paths and format
|
||||
|
||||
## 0.3.0 - 2019-26-01
|
||||
- Add amalgamated build to janet for easier embedding.
|
||||
- Add os/date function
|
||||
- Add slurp and spit to core library.
|
||||
- Added this changelog.
|
||||
- Added peg module (Parsing Expression Grammars)
|
||||
- Move hand written documentation into website repository.
|
||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2019 Calvin Rose
|
||||
Copyright (c) 2019 Calvin Rose and contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
|
||||
67
Makefile
67
Makefile
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2018 Calvin Rose
|
||||
# Copyright (c) 2019 Calvin Rose
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
@@ -47,7 +47,7 @@ else
|
||||
CLIBS:=$(CLIBS) -lrt
|
||||
endif
|
||||
|
||||
$(shell mkdir -p build/core build/mainclient build/webclient)
|
||||
$(shell mkdir -p build/core build/mainclient build/webclient build/boot)
|
||||
|
||||
# Source headers
|
||||
JANET_HEADERS=$(sort $(wildcard src/include/janet/*.h))
|
||||
@@ -60,14 +60,33 @@ JANET_WEBCLIENT_SOURCES=$(sort $(wildcard src/webclient/*.c))
|
||||
|
||||
all: $(JANET_TARGET) $(JANET_LIBRARY)
|
||||
|
||||
##################################################################
|
||||
##### The bootstrap interpreter that compiles the core image #####
|
||||
##################################################################
|
||||
|
||||
JANET_BOOT_OBJECTS=$(patsubst src/%.c,build/%.boot.o,$(JANET_CORE_SOURCES) src/boot/boot.c) \
|
||||
build/core.gen.o \
|
||||
build/boot.gen.o
|
||||
|
||||
build/%.boot.o: src/%.c
|
||||
$(CC) $(CFLAGS) -DJANET_BOOTSTRAP -o $@ -c $<
|
||||
|
||||
build/janet_boot: $(JANET_BOOT_OBJECTS)
|
||||
$(CC) $(CFLAGS) -DJANET_BOOTSTRAP -o $@ $^ $(CLIBS)
|
||||
|
||||
# Now the reason we bootstrap in the first place
|
||||
build/core_image.c: build/janet_boot
|
||||
build/janet_boot
|
||||
|
||||
##########################################################
|
||||
##### The main interpreter program and shared object #####
|
||||
##########################################################
|
||||
|
||||
JANET_CORE_OBJECTS=$(patsubst src/%.c,build/%.o,$(JANET_CORE_SOURCES)) build/core.gen.o
|
||||
JANET_CORE_OBJECTS=$(patsubst src/%.c,build/%.o,$(JANET_CORE_SOURCES)) build/core_image.o
|
||||
JANET_MAINCLIENT_OBJECTS=$(patsubst src/%.c,build/%.o,$(JANET_MAINCLIENT_SOURCES)) build/init.gen.o
|
||||
|
||||
%.gen.o: %.gen.c
|
||||
# Compile the core image generated by the bootstrap build
|
||||
build/core_image.o: build/core_image.c $(JANET_HEADERS) $(JANET_LOCAL_HEADERS)
|
||||
$(CC) $(CFLAGS) -o $@ -c $<
|
||||
|
||||
build/%.o: src/%.c $(JANET_HEADERS) $(JANET_LOCAL_HEADERS)
|
||||
@@ -92,11 +111,14 @@ EMCFLAGS=-std=c99 -Wall -Wextra -Isrc/include -O2 \
|
||||
JANET_EMTARGET=build/janet.js
|
||||
JANET_WEB_SOURCES=$(JANET_CORE_SOURCES) $(JANET_WEBCLIENT_SOURCES)
|
||||
JANET_EMOBJECTS=$(patsubst src/%.c,build/%.bc,$(JANET_WEB_SOURCES)) \
|
||||
build/webinit.gen.bc build/core.gen.bc
|
||||
build/webinit.gen.bc build/core_image.bc
|
||||
|
||||
%.gen.bc: %.gen.c
|
||||
$(EMCC) $(EMCFLAGS) -o $@ -c $<
|
||||
|
||||
build/core_image.bc: build/core_image.c $(JANET_HEADERS) $(JANET_LOCAL_HEADERS)
|
||||
$(EMCC) $(EMCFLAGS) -o $@ -c $<
|
||||
|
||||
build/%.bc: src/%.c $(JANET_HEADERS) $(JANET_LOCAL_HEADERS)
|
||||
$(EMCC) $(EMCFLAGS) -o $@ -c $<
|
||||
|
||||
@@ -109,6 +131,9 @@ emscripten: $(JANET_EMTARGET)
|
||||
##### Generated C files #####
|
||||
#############################
|
||||
|
||||
%.gen.o: %.gen.c
|
||||
$(CC) $(CFLAGS) -o $@ -c $<
|
||||
|
||||
build/xxd: tools/xxd.c
|
||||
$(CC) $< -o $@
|
||||
|
||||
@@ -118,18 +143,27 @@ build/init.gen.c: src/mainclient/init.janet build/xxd
|
||||
build/xxd $< $@ janet_gen_init
|
||||
build/webinit.gen.c: src/webclient/webinit.janet build/xxd
|
||||
build/xxd $< $@ janet_gen_webinit
|
||||
build/boot.gen.c: src/boot/boot.janet build/xxd
|
||||
build/xxd $< $@ janet_gen_boot
|
||||
|
||||
########################
|
||||
##### Amalgamation #####
|
||||
########################
|
||||
|
||||
amalg: build/janet.c build/janet.h build/core_image.c
|
||||
|
||||
build/janet.c: $(JANET_LOCAL_HEADERS) $(JANET_CORE_SOURCES) tools/amalg.janet $(JANET_TARGET)
|
||||
$(JANET_TARGET) tools/amalg.janet > $@
|
||||
|
||||
build/janet.h: src/include/janet/janet.h
|
||||
cp $< $@
|
||||
|
||||
###################
|
||||
##### Testing #####
|
||||
###################
|
||||
|
||||
TEST_SOURCES=$(wildcard ctest/*.c)
|
||||
TEST_PROGRAMS=$(patsubst ctest/%.c,build/%.out,$(TEST_SOURCES))
|
||||
TEST_SCRIPTS=$(wildcard test/suite*.janet)
|
||||
|
||||
build/%.out: ctest/%.c $(JANET_CORE_OBJECTS)
|
||||
$(CC) $(CFLAGS) -o $@ $^ $(CLIBS)
|
||||
|
||||
repl: $(JANET_TARGET)
|
||||
./$(JANET_TARGET)
|
||||
|
||||
@@ -142,11 +176,9 @@ valgrind: $(JANET_TARGET)
|
||||
$(VALGRIND_COMMAND) ./$(JANET_TARGET)
|
||||
|
||||
test: $(JANET_TARGET) $(TEST_PROGRAMS)
|
||||
for f in build/*.out; do "$$f" || exit; done
|
||||
for f in test/*.janet; do ./$(JANET_TARGET) "$$f" || exit; done
|
||||
|
||||
valtest: $(JANET_TARGET) $(TEST_PROGRAMS)
|
||||
for f in build/*.out; do $(VALGRIND_COMMAND) "$$f" || exit; done
|
||||
for f in test/*.janet; do $(VALGRIND_COMMAND) ./$(JANET_TARGET) "$$f" || exit; done
|
||||
|
||||
callgrind: $(JANET_TARGET)
|
||||
@@ -160,7 +192,7 @@ dist: build/janet-dist.tar.gz
|
||||
|
||||
build/janet-%.tar.gz: $(JANET_TARGET) src/include/janet/janet.h \
|
||||
janet.1 LICENSE CONTRIBUTING.md $(JANET_LIBRARY) \
|
||||
build/doc.html README.md $(wildcard doc/*.md)
|
||||
build/doc.html README.md build/janet.c
|
||||
tar -czvf $@ $^
|
||||
|
||||
#########################
|
||||
@@ -176,6 +208,10 @@ build/doc.html: $(JANET_TARGET) tools/gendoc.janet
|
||||
##### Other #####
|
||||
#################
|
||||
|
||||
grammar: build/janet.tmLanguage
|
||||
build/janet.tmLanguage: tools/tm_lang_gen.janet $(JANET_TARGET)
|
||||
$(JANET_TARGET) $< > $@
|
||||
|
||||
clean:
|
||||
-rm -rf build vgcore.* callgrind.*
|
||||
|
||||
@@ -188,6 +224,7 @@ install: $(JANET_TARGET)
|
||||
cp $(JANET_LIBRARY) $(LIBDIR)/libjanet.so
|
||||
mkdir -p $(JANET_PATH)
|
||||
cp tools/cook.janet $(JANET_PATH)
|
||||
cp tools/highlight.janet $(JANET_PATH)
|
||||
cp janet.1 /usr/local/share/man/man1/
|
||||
mandb
|
||||
$(LDCONFIG)
|
||||
@@ -198,6 +235,6 @@ uninstall:
|
||||
-rm -rf $(INCLUDEDIR)
|
||||
$(LDCONFIG)
|
||||
|
||||
.PHONY: clean install repl debug valgrind test \
|
||||
valtest emscripten dist uninstall docs \
|
||||
.PHONY: clean install repl debug valgrind test amalg \
|
||||
valtest emscripten dist uninstall docs grammar \
|
||||
$(TEST_PROGRAM_PHONIES) $(TEST_PROGRAM_VALPHONIES)
|
||||
|
||||
33
README.md
33
README.md
@@ -1,3 +1,5 @@
|
||||
[](https://gitter.im/janet-language/community)
|
||||
|
||||
[](https://travis-ci.org/janet-lang/janet)
|
||||
[](https://ci.appveyor.com/project/janet-lang/janet)
|
||||
|
||||
@@ -20,8 +22,8 @@ The few features that are not standard C (dynamic library loading, compiler spec
|
||||
are fairly straight forward. Janet can be easily ported to new platforms.
|
||||
|
||||
For syntax highlighting, there is some preliminary vim syntax highlighting in [janet.vim](https://github.com/janet-lang/janet.vim).
|
||||
Generic lisp syntax highlighting should, however, provide good results. There is also a janet.tmLanguage file
|
||||
that should provide good syntax highlighting for many editors.
|
||||
Generic lisp syntax highlighting should, however, provide good results. One can also generate a janet.tmLanguage
|
||||
file for other programs with `make grammar`.
|
||||
|
||||
## Use Cases
|
||||
|
||||
@@ -46,7 +48,9 @@ Janet makes a good system scripting language, or a language to embed in other pr
|
||||
* Lexical scoping
|
||||
* Imperative programming as well as functional
|
||||
* REPL
|
||||
* Parsing Expression Grammars built in to the core library
|
||||
* 300+ functions and macros in the core library
|
||||
* Embedding Janet in other programs
|
||||
* Interactive environment with detailed stack traces
|
||||
|
||||
## Documentation
|
||||
@@ -74,7 +78,8 @@ environment, use the `(all-symbols)` function.
|
||||
|
||||
Install a stable version of janet from the [releases page](https://github.com/janet-lang/janet/releases).
|
||||
Janet is prebuilt for a few systems, but if you want to develop janet, run janet on a non-x86 system, or
|
||||
get the latest, you must build janet from source.
|
||||
get the latest, you must build janet from source. Janet is in alpha and may change
|
||||
in backwards incompatible ways.
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -82,7 +87,7 @@ A repl is launched when the binary is invoked with no arguments. Pass the -h fla
|
||||
to display the usage information. Individual scripts can be run with `./janet myscript.janet`
|
||||
|
||||
If you are looking to explore, you can print a list of all available macros, functions, and constants
|
||||
by entering the command `(all-symbols)` into the repl.
|
||||
by entering the command `(all-bindings)` into the repl.
|
||||
|
||||
```
|
||||
$ ./janet
|
||||
@@ -106,13 +111,25 @@ Options are:
|
||||
$
|
||||
```
|
||||
|
||||
## Embedding
|
||||
|
||||
The C API for Janet is not yet documented but coming soon.
|
||||
|
||||
Janet can be embedded in a host program very easily. There is a make target `make amalg`
|
||||
which creates the file `build/janet.c`, which is a single C file that contains all the source
|
||||
to Janet. This file, along with `src/include/janet/janet.h` can dragged into any C project
|
||||
and compiled into the project. Janet should be compiled with `-std=c99` on most compilers, and
|
||||
will need to be linked to the math library, `-lm`, and the dynamic linker, `-ldl`, if one wants
|
||||
to be able to load dynamic modules. If there is no need for dynamic modules, add the define
|
||||
`-DJANET_NO_DYNAMIC_MODULES` to the compiler options.
|
||||
|
||||
## Compiling and Running
|
||||
|
||||
Janet only uses Make and batch files to compile on Posix and windows
|
||||
respectively. To configure janet, edit the header file src/include/janet/janet.h
|
||||
before compilation.
|
||||
|
||||
### Unix-like
|
||||
### macos and Unix-like
|
||||
|
||||
On most platforms, use Make to build janet. The resulting binary will be in `build/janet`.
|
||||
|
||||
@@ -160,8 +177,14 @@ Building with emscripten on windows is currently unsupported.
|
||||
|
||||
See the examples directory for some example janet code.
|
||||
|
||||
## Discussion
|
||||
|
||||
Feel free to ask questions and join discussion on the [Janet Gitter Channel](https://gitter.im/janet-language/community).
|
||||
Alternatively, check out [the #janet channel on Freenode](https://webchat.freenode.net/)
|
||||
|
||||
## Why Janet
|
||||
|
||||
Janet is named after the almost omniscient and friendly artificial being in [The Good Place](https://en.wikipedia.org/wiki/The_Good_Place).
|
||||
|
||||
<img src="https://raw.githubusercontent.com/janet-lang/janet/master/assets/janet-the-good-place.gif" alt="Janet logo" width="115px" align="left">
|
||||
|
||||
|
||||
@@ -45,4 +45,4 @@ deploy:
|
||||
artifact: janet-windows
|
||||
draft: true
|
||||
on:
|
||||
APPVEYOR_REPO_TAG: true
|
||||
APPVEYOR_REPO_TAG: true
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
mkdir build
|
||||
mkdir build\core
|
||||
mkdir build\mainclient
|
||||
mkdir build\boot
|
||||
|
||||
@rem Build the xxd tool for generating sources
|
||||
@cl /nologo /c tools/xxd.c /Fobuild\xxd.obj
|
||||
@@ -30,15 +31,36 @@ mkdir build\mainclient
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
|
||||
@rem Generate the embedded sources
|
||||
@build\xxd.exe src\core\core.janet build\core\core.gen.c janet_gen_core
|
||||
@build\xxd.exe src\core\core.janet build\core.gen.c janet_gen_core
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
@build\xxd.exe src\mainclient\init.janet build\mainclient\init.gen.c janet_gen_init
|
||||
@build\xxd.exe src\mainclient\init.janet build\init.gen.c janet_gen_init
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
@build\xxd.exe src\boot\boot.janet build\boot.gen.c janet_gen_boot
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
|
||||
@rem Build the generated sources
|
||||
@%JANET_COMPILE% /Fobuild\core\core.gen.obj build\core\core.gen.c
|
||||
@%JANET_COMPILE% /Fobuild\core\core.gen.obj build\core.gen.c
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
@%JANET_COMPILE% /Fobuild\mainclient\init.gen.obj build\mainclient\init.gen.c
|
||||
@%JANET_COMPILE% /Fobuild\mainclient\init.gen.obj build\init.gen.c
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
@%JANET_COMPILE% /Fobuild\boot\boot.gen.obj build\boot.gen.c
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
|
||||
@rem Build the bootstrap interpretter
|
||||
for %%f in (src\core\*.c) do (
|
||||
@%JANET_COMPILE% /DJANET_BOOTSTRAP /Fobuild\boot\%%~nf.obj %%f
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
)
|
||||
for %%f in (src\boot\*.c) do (
|
||||
@%JANET_COMPILE% /DJANET_BOOTSTRAP /Fobuild\boot\%%~nf.obj %%f
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
)
|
||||
%JANET_LINK% /out:build\janet_boot.exe build\boot\*.obj
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
build\janet_boot
|
||||
|
||||
@rem Build the core image
|
||||
@%JANET_COMPILE% /Fobuild\core_image.obj build\core_image.c
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
|
||||
@rem Build the sources
|
||||
@@ -54,7 +76,7 @@ for %%f in (src\mainclient\*.c) do (
|
||||
)
|
||||
|
||||
@rem Link everything to main client
|
||||
%JANET_LINK% /out:janet.exe build\core\*.obj build\mainclient\*.obj
|
||||
%JANET_LINK% /out:janet.exe build\core\*.obj build\mainclient\*.obj build\core_image.obj
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
|
||||
echo === Successfully built janet.exe for Windows ===
|
||||
@@ -94,12 +116,15 @@ exit /b 0
|
||||
:DIST
|
||||
mkdir dist
|
||||
janet.exe tools\gendoc.janet > dist\doc.html
|
||||
janet.exe tools\amalg.janet > dist\janet.c
|
||||
copy janet.exe dist\janet.exe
|
||||
copy LICENSE dist\LICENSE
|
||||
copy README.md dist\README.md
|
||||
copy janet.lib dist\janet.lib
|
||||
copy janet.exp dist\janet.exp
|
||||
copy src\include\janet\janet.h dist\janet.h
|
||||
copy tools\cook.janet dist\cook.janet
|
||||
copy tools\highlight.janet dist\highlight.janet
|
||||
exit /b 0
|
||||
|
||||
:TESTFAIL
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Calvin Rose
|
||||
* Copyright (c) 2019 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
Janet is a dynamic, lightweight programming language with strong functional
|
||||
capabilities as well as support for imperative programming. It to be used
|
||||
for short lived scripts as well as for building real programs. It can also
|
||||
be extended with native code (C modules) for better performance and interfacing with
|
||||
existing software. Janet takes ideas from Lua, Scheme, Racket, Clojure, Smalltalk, Erlang, Arc, and
|
||||
a whole bunch of other dynamic languages.
|
||||
@@ -1,739 +0,0 @@
|
||||
# Hello, world!
|
||||
|
||||
Following tradition, a simple Janet program will print "Hello, world!".
|
||||
|
||||
```
|
||||
(print "Hello, world!")
|
||||
```
|
||||
|
||||
Put the following code in a file named `hello.janet`, and run `./janet hello.janet`.
|
||||
The words "Hello, world!" should be printed to the console, and then the program
|
||||
should immediately exit. You now have a working janet program!
|
||||
|
||||
Alternatively, run the program `./janet` without any arguments to enter a REPL,
|
||||
or read eval print loop. This is a mode where Janet functions like a calculator,
|
||||
reading some input from the user, evaluating it, and printing out the result, all
|
||||
in an infinite loop. This is a useful mode for exploring or prototyping in Janet.
|
||||
|
||||
This hello world program is about the simplest program one can write, and consists of only
|
||||
a few pieces of syntax. This first element is the `print` symbol. This is a function
|
||||
that simply prints its arguments to the console. The second argument is the
|
||||
string literal "Hello, world!", which is the one and only argument to the
|
||||
print function. Lastly, the print symbol and the string literal are wrapped
|
||||
in parentheses, forming a tuple. In Janet, parentheses and brackets are interchangeable,
|
||||
brackets are used mostly when the resulting tuple is not a function call. The tuple
|
||||
above indicates that the function `print` is to be called with one argument, `"Hello, world"`.
|
||||
|
||||
Like all lisps, all operations in Janet are in prefix notation; the name of the
|
||||
operator is the first value in the tuple, and the arguments passed to it are
|
||||
in the rest of the tuple.
|
||||
|
||||
# A bit more - Arithmetic
|
||||
|
||||
Any programming language will have some way to do arithmetic. Janet is no exception,
|
||||
and supports the basic arithmetic operators
|
||||
|
||||
```
|
||||
# Prints 13
|
||||
# (1 + (2*2) + (10/5) + 3 + 4 + (5 - 6))
|
||||
(print (+ 1 (* 2 2) (/ 10 5) 3 4 (- 5 6)))
|
||||
```
|
||||
|
||||
Just like the print function, all arithmetic operators are entered in
|
||||
prefix notation. Janet also supports the remainder operator, or `%`, which returns
|
||||
the remainder of division. For example, `(% 10 3)` is 1, and `(% 10.5 3)` is
|
||||
1.5. The lines that begin with `#` are comments.
|
||||
|
||||
All janet numbers are IEEE 754 floating point numbers. They can be used to represent
|
||||
both integers and real numbers to a finite precision.
|
||||
|
||||
## Numeric literals
|
||||
|
||||
Numeric literals can be written in many ways. Numbers can be written in base 10, with
|
||||
underscores used to separate digits into groups. A decimal point can be used for floating
|
||||
point numbers. Numbers can also be written in other bases by prefixing the number with the desired
|
||||
base and the character 'r'. For example, 16 can be written as `16`, `1_6`, `16r10`, `4r100`, or `0x10`. The
|
||||
`0x` prefix can be used for hexadecimal as it is so common. The radix must be themselves written in base 10, and
|
||||
can be any integer from 2 to 36. For any radix above 10, use the letters as digits (not case sensitive).
|
||||
|
||||
Numbers can also be in scientific notation such as `3e10`. A custom radix can be used as well
|
||||
as for scientific notation numbers, (the exponent will share the radix). For numbers in scientific
|
||||
notation with a radix besides 10, use the `&` symbol to indicate the exponent rather then `e`.
|
||||
|
||||
## Arithmetic Functions
|
||||
|
||||
Besides the 5 main arithmetic functions, janet also supports a number of math functions
|
||||
taken from the C library `<math.h>`, as well as bit-wise operators that behave like they
|
||||
do in C or Java. Functions like `math/sin`, `math/cos`, `math/log`, and `math/exp` will
|
||||
behave as expected to a C programmer. They all take either 1 or 2 numeric arguments and
|
||||
return a real number (never an integer!) Bit-wise functions are all prefixed with b.
|
||||
They are `bnot`, `bor`, `bxor`, `band`, `blshift`, `brshift`, and `brushift`. Bit-wise
|
||||
functions only work on integers.
|
||||
|
||||
# Strings, Keywords and Symbols
|
||||
|
||||
Janet supports several varieties of types that can be used as labels for things in
|
||||
your program. The most useful type for this purpose is the keyword type. A keyword
|
||||
begins with a semicolon, and then contains 0 or more alphanumeric or a few other common
|
||||
characters. For example, `:hello`, `:my-name`, `::`, and `:ABC123_-*&^%$` are all keywords.
|
||||
|
||||
Keywords, symbols, and strings all behave similarly and can be used as keys for tables and structs.
|
||||
Symbols and keywords are optimized for fast equality checks, so are preferred for table keys.
|
||||
|
||||
The difference between symbols and keywords is that keywords evaluate to themselves, while
|
||||
symbols evaluate to whatever they are bound to. To have a symbol evaluate to itself, it must be
|
||||
quoted.
|
||||
|
||||
```lisp
|
||||
# Evaluates to :monday
|
||||
:monday
|
||||
|
||||
# Will throw a compile error as monday is not defined
|
||||
monday
|
||||
|
||||
# Quote it - evaluates to the symbol monday
|
||||
'monday
|
||||
|
||||
# Or first define monday
|
||||
(def monday "It is monday")
|
||||
|
||||
# Now the evaluation should work - monday evaluates to "It is monday"
|
||||
monday
|
||||
```
|
||||
|
||||
The most common thing to do with a keyword is to check it for equality or use it as a key into
|
||||
a table or struct. Note that symbols, keywords and strings are all immutable. Besides making your
|
||||
code easier to reason about, it allows for many optimizations involving these types.
|
||||
|
||||
```lisp
|
||||
# Evaluates to true
|
||||
(= :hello :hello)
|
||||
|
||||
# Evaluates to false, everything in janet is case sensitive
|
||||
(= :hello :HeLlO)
|
||||
|
||||
# Look up into a table - evaluates to 25
|
||||
(get {
|
||||
:name "John"
|
||||
:age 25
|
||||
:occupation "plumber"
|
||||
} :age)
|
||||
```
|
||||
|
||||
Strings can be used similarly to keywords, but there primary usage is for defining either text
|
||||
or arbitrary sequences of bytes. Strings (and symbols) in janet are what is sometimes known as
|
||||
"8-bit clean"; they can hold any number of bytes, and are completely unaware of things like character
|
||||
encodings. This is completely compatible with ASCII and UTF-8, two of the most common character
|
||||
encodings. By being encoding agnostic, janet strings can be very simple, fast, and useful for
|
||||
for other uses besides holding text.
|
||||
|
||||
Literal text can be entered inside quotes, as we have seen above.
|
||||
|
||||
```
|
||||
"Hello, this is a string."
|
||||
|
||||
# We can also add escape characters for newlines, double quotes, backslash, tabs, etc.
|
||||
"Hello\nThis is on line two\n\tThis is indented\n"
|
||||
|
||||
# For long strings where you don't want to type a lot of escape characters,
|
||||
# you can use 1 or more backticks (`\``) to delimit a string.
|
||||
# To close this string, simply repeat the opening sequence of backticks
|
||||
``
|
||||
This is a string.
|
||||
Line 2
|
||||
Indented
|
||||
"We can just type quotes here", and backslashes \ no problem.
|
||||
``
|
||||
```
|
||||
|
||||
# Functions
|
||||
|
||||
Janet is a functional language - that means that one of the basic building blocks of your
|
||||
program will be defining functions (the other is using data structures). Because janet
|
||||
is a Lisp, functions are values just like numbers or strings - they can be passed around and
|
||||
created as needed.
|
||||
|
||||
Functions can be defined with the `defn` macro, like so:
|
||||
|
||||
```lisp
|
||||
(defn triangle-area
|
||||
"Calculates the area of a triangle."
|
||||
[base height]
|
||||
(print "calculating area of a triangle...")
|
||||
(* base height 0.5))
|
||||
```
|
||||
|
||||
A function defined with `defn` consists of a name, a number of optional flags for def, and
|
||||
finally a function body. The example above is named triangle-area and takes two parameters named base and height. The body of the function will print a message and then evaluate to the area of the triangle.
|
||||
|
||||
Once a function like the above one is defined, the programmer can use the `triangle-area`
|
||||
function just like any other, say `print` or `+`.
|
||||
|
||||
```lisp
|
||||
# Prints "calculating area of a triangle..." and then "25"
|
||||
(print (triangle-area 5 10))
|
||||
```
|
||||
|
||||
Note that when nesting function calls in other function calls like above (a call to triangle-area is
|
||||
nested inside a call to print), the inner function calls are evaluated first. Also, arguments to
|
||||
a function call are evaluated in order, from first argument to last argument).
|
||||
|
||||
Because functions are first-class values like numbers or strings, they can be passed
|
||||
as arguments to other functions as well.
|
||||
|
||||
```lisp
|
||||
(print triangle-area)
|
||||
```
|
||||
|
||||
This prints the location in memory of the function triangle area.
|
||||
|
||||
Functions don't need to have names. The `fn` keyword can be used to introduce function
|
||||
literals without binding them to a symbol.
|
||||
|
||||
```lisp
|
||||
# Evaluates to 40
|
||||
((fn [x y] (+ x x y)) 10 20)
|
||||
# Also evaluates to 40
|
||||
((fn [x y &] (+ x x y)) 10 20)
|
||||
|
||||
# Will throw an error about the wrong arity
|
||||
((fn [x] x) 1 2)
|
||||
# Will not throw an error about the wrong arity
|
||||
((fn [x &] x) 1 2)
|
||||
```
|
||||
|
||||
The first expression creates an anonymous function that adds twice
|
||||
the first argument to the second, and then calls that function with arguments 10 and 20.
|
||||
This will return (10 + 10 + 20) = 40.
|
||||
|
||||
There is a common macro `defn` that can be used for creating functions and immediately binding
|
||||
them to a name. `defn` works as expected at both the top level and inside another form. There is also
|
||||
the corresponding
|
||||
|
||||
Note that putting an ampersand at the end of the argument list inhibits strict arity checking.
|
||||
This means that such a function will accept fewer or more arguments than specified.
|
||||
|
||||
```lisp
|
||||
(defn myfun [x y]
|
||||
(+ x x y))
|
||||
|
||||
# You can think of defn as a shorthand for def and fn together
|
||||
(def myfun-same (fn [x y]
|
||||
(+ x x Y)))
|
||||
|
||||
(myfun 3 4) # -> 10
|
||||
```
|
||||
|
||||
Janet has many macros provided for you (and you can write your own).
|
||||
Macros are just functions that take your source code
|
||||
and transform it into some other source code, usually automating some repetitive pattern for you.
|
||||
|
||||
# Defs and Vars
|
||||
|
||||
Values can be bound to symbols for later use using the keyword `def`. Using undefined
|
||||
symbols will raise an error.
|
||||
|
||||
```lisp
|
||||
(def a 100)
|
||||
(def b (+ 1 a))
|
||||
(def c (+ b b))
|
||||
(def d (- c 100))
|
||||
```
|
||||
|
||||
Bindings created with def have lexical scoping. Also, bindings created with def are immutable; they
|
||||
cannot be changed after definition. For mutable bindings, like variables in other programming
|
||||
languages, use the `var` keyword. The assignment special form `set` can then be used to update
|
||||
a var.
|
||||
|
||||
```lisp
|
||||
(var myvar 1)
|
||||
(print myvar)
|
||||
(set myvar 10)
|
||||
(print myvar)
|
||||
```
|
||||
|
||||
In the global scope, you can use the `:private` option on a def or var to prevent it from
|
||||
being exported to code that imports your current module. You can also add documentation to
|
||||
a function by passing a string the def or var command.
|
||||
|
||||
```lisp
|
||||
(def mydef :private "This will have priavte scope. My doc here." 123)
|
||||
(var myvar "docstring here" 321)
|
||||
```
|
||||
|
||||
## Scopes
|
||||
|
||||
Defs and vars (collectively known as bindings) live inside what is called a scope. A scope is
|
||||
simply where the bindings are valid. If a binding is referenced outside of its scope, the compiler
|
||||
will throw an error. Scopes are useful for organizing your bindings and my extension your programs.
|
||||
There are two main ways to create a scope in Janet.
|
||||
|
||||
The first is to use the `do` special form. `do` executes a series of statements in a scope
|
||||
and evaluates to the last statement. Bindings create inside the form do not escape outside
|
||||
of its scope.
|
||||
|
||||
```lisp
|
||||
(def a :outera)
|
||||
|
||||
(do
|
||||
(def a 1)
|
||||
(def b 2)
|
||||
(def c 3)
|
||||
(+ a b c)) # -> 6
|
||||
|
||||
a # -> :outera
|
||||
b # -> compile error: "unknown symbol \"b\""
|
||||
c # -> compile error: "unknown symbol \"c\""
|
||||
```
|
||||
|
||||
Any attempt to reference the bindings from the do form after it has finished
|
||||
executing will fail. Also notice who defining `a` inside the do form did not
|
||||
overwrite the original definition of `a` for the global scope.
|
||||
|
||||
The second way to create a scope is to create a closure.
|
||||
The `fn` special form also introduces a scope just like
|
||||
the `do` special form.
|
||||
|
||||
There is another built in macro, `let`, that does multiple defs at once, and then introduces a scope.
|
||||
`let` is a wrapper around a combination of defs and dos, and is the most "functional" way of
|
||||
creating bindings.
|
||||
|
||||
```lisp
|
||||
(let [a 1
|
||||
b 2
|
||||
c 3]
|
||||
(+ a b c)) # -> 6
|
||||
```
|
||||
|
||||
The above is equivalent to the example using `do` and `def`.
|
||||
This is the preferable form in most cases,
|
||||
but using do with multiple defs is fine as well.
|
||||
|
||||
# Data Structures
|
||||
|
||||
Once you have a handle on functions and the primitive value types, you may be wondering how
|
||||
to work with collections of things. Janet has a small number of core data structure types
|
||||
that are very versatile. Tables, Structs, Arrays, Tuples, Strings, and Buffers, are the 6 main
|
||||
built in data structure types. These data structures can be arranged in a useful table describing
|
||||
there relationship to each other.
|
||||
|
||||
| | Mutable | Immutable |
|
||||
| ---------- | ------- | --------------- |
|
||||
| Indexed | Array | Tuple |
|
||||
| Dictionary | Table | Struct |
|
||||
| Bytes | Buffer | String |
|
||||
|
||||
Indexed types are linear lists of elements than can be accessed in constant time with an integer index.
|
||||
Indexed types are backed by a single chunk of memory for fast access, and are indexed from 0 as in C.
|
||||
Dictionary types associate keys with values. The difference between dictionaries and indexed types
|
||||
is that dictionaries are not limited to integer keys. They are backed by a hashtable and also offer
|
||||
constant time lookup (and insertion for the mutable case).
|
||||
Finally, the 'bytes' abstraction is any type that contains a sequence of bytes. A 'bytes' value or byteseq associates
|
||||
integer keys (the indices) with integer values between 0 and 255 (the byte values). In this way,
|
||||
they behave much like Arrays and Tuples. However, one cannot put non integer values into a byteseq
|
||||
|
||||
```lisp
|
||||
(def mytuple (tuple 1 2 3))
|
||||
|
||||
(def myarray @(1 2 3))
|
||||
(def myarray (array 1 2 3))
|
||||
|
||||
(def mystruct {
|
||||
:key "value"
|
||||
:key2 "another"
|
||||
1 2
|
||||
4 3})
|
||||
|
||||
(def another-struct
|
||||
(struct :a 1 :b 2))
|
||||
|
||||
(def my-table @{
|
||||
:a :b
|
||||
:c :d
|
||||
:A :qwerty})
|
||||
(def another-table
|
||||
(table 1 2 3 4))
|
||||
|
||||
(def my-buffer @"thisismutable")
|
||||
(def my-buffer2 @```
|
||||
This is also mutable ":)"
|
||||
```)
|
||||
```
|
||||
|
||||
To read the values in a data structure, use the get function. The first parameter is the data structure
|
||||
itself, and the second parameter is the key.
|
||||
|
||||
```lisp
|
||||
(get @{:a 1} :a) # -> 1
|
||||
(get {:a 1} :a) # -> 1
|
||||
(get @[:a :b :c] 2) # -> :c
|
||||
(get (tuple "a" "b" "c") 1) # -> "b"
|
||||
(get @"hello, world" 1) # -> 101
|
||||
(get "hello, world" 0) # -> 104
|
||||
```
|
||||
|
||||
### Destructuring
|
||||
|
||||
In many cases, however, you do not need the `get` function at all. Janet supports destructuring, which
|
||||
means both the `def` and `var` special forms can extract values from inside structures themselves.
|
||||
|
||||
```lisp
|
||||
# Before, we might do
|
||||
(def my-array @[:mary :had :a :little :lamb])
|
||||
(def lamb (get my-array 4))
|
||||
(print lamb) # Prints :lamb
|
||||
|
||||
# Now, with destructuring,
|
||||
(def [_ _ _ _ lamb] my-array)
|
||||
(print lamb) # Again, prints :lamb
|
||||
|
||||
# Destructuring works with tables as well
|
||||
(def person @{:name "Bob Dylan" :age 77}
|
||||
(def
|
||||
{:name person-name
|
||||
:age person-age} person)
|
||||
```
|
||||
To update a mutable data structure, use the `put` function. It takes 3 arguments, the data structure,
|
||||
the key, and the value, and returns the data structure. The allowed types keys and values
|
||||
depend on what data structure is passed in.
|
||||
|
||||
```lisp
|
||||
(put @[] 100 :a)
|
||||
(put @{} :key "value")
|
||||
(put @"" 100 92)
|
||||
```
|
||||
|
||||
Note that for Arrays and Buffers, putting an index that is outside the length of the data structure
|
||||
will extend the data structure and fill it with nils in the case of the Array,
|
||||
or 0s in the case of the Buffer.
|
||||
|
||||
The last generic function for all data structures is the `length` function. This returns the number of
|
||||
values in a data structure (the number of keys in a dictionary type).
|
||||
|
||||
# Flow Control
|
||||
|
||||
Janet has only two built in primitives to change flow while inside a function. The first is the
|
||||
`if` special form, which behaves as expected in most functional languages. It takes two or three parameters:
|
||||
a condition, an expression to evaluate to if the condition is true (not nil or false),
|
||||
and an optional condition to evaluate to when the condition is nil or false. If the optional parameter
|
||||
is omitted, the if form evaluates to nil.
|
||||
|
||||
```lisp
|
||||
(if (> 4 3)
|
||||
"4 is greater than 3"
|
||||
"4 is not greater then three") # Evaluates to the first statement
|
||||
|
||||
(if true
|
||||
(print "Hey")) # Will print
|
||||
|
||||
(if false
|
||||
(print "Oy!")) # Will not print
|
||||
```
|
||||
|
||||
The second primitive control flow construct is the while loop. The while behaves much the same
|
||||
as in many other programming languages, including C, Java, and Python. The while loop takes
|
||||
two or more parameters: the first is a condition (like in the `if` statement), that is checked before
|
||||
every iteration of the loop. If it is nil or false, the while loop ends and evaluates to nil. Otherwise,
|
||||
the rest of the parameters will be evaluated sequentially and then the program will return to the beginning
|
||||
of the loop.
|
||||
|
||||
```lisp
|
||||
# Loop from 100 down to 1 and print each time
|
||||
(var i 100)
|
||||
(while (pos? i)
|
||||
(print "the number is " i)
|
||||
(-- i))
|
||||
|
||||
# Print ... until a random number in range [0, 1) is >= 0.9
|
||||
# (math/random evaluates to a value between 0 and 1)
|
||||
(while (> 0.9 (math/random))
|
||||
(print "..."))
|
||||
```
|
||||
|
||||
Besides these special forms, Janet has many macros for both conditional testing and looping
|
||||
that are much better for the majority of cases. For conditional testing, the `cond`, `switch`, and
|
||||
`when` macros can be used to great effect. `cond` can be used for making an if-else chain, where using
|
||||
just raw if forms would result in many parentheses. `case` For looping, the `loop`, `seq`, and `generate`
|
||||
implement janet's form of list comprehension, as in Python or Clojure.
|
||||
|
||||
# The Core Library
|
||||
|
||||
Janet has a built in core library of over 300 functions and macros at the time of writing.
|
||||
While some of these functions may be refactored into separate modules, it is useful to get to know
|
||||
the core to avoid rewriting provided functions.
|
||||
|
||||
For any given function, use the `doc` macro to view the documentation for it in the repl.
|
||||
|
||||
```lisp
|
||||
(doc defn) -> Prints the documentation for "defn"
|
||||
```
|
||||
To see a list of all global functions in the repl, type the command
|
||||
|
||||
```lisp
|
||||
(table/getproto *env*)
|
||||
# Or
|
||||
(all-symbols)
|
||||
```
|
||||
Which will print out every built-in global binding
|
||||
(it will not show your global bindings). To print all
|
||||
of your global bindings, just use \*env\*, which is a var
|
||||
that is bound to the current environment.
|
||||
|
||||
The convention of surrounding a symbol in stars is taken from lisp
|
||||
and Clojure, and indicates a global dynamic variable rather than a normal
|
||||
definition. To get the static environment at the time of compilation, use the
|
||||
`_env` symbol.
|
||||
|
||||
# Prototypes
|
||||
|
||||
To support basic generic programming, Janet tables support a prototype
|
||||
table. A prototype table contains default values for a table if certain keys
|
||||
are not found in the original table. This allows many similar tables to share
|
||||
contents without duplicating memory.
|
||||
|
||||
```lisp
|
||||
# One of many Object Oriented schemes that can
|
||||
# be implented in janet.
|
||||
(def proto1 @{:type :custom1
|
||||
:behave (fn [self x] (print "behaving " x))})
|
||||
(def proto2 @{:type :custom2
|
||||
:behave (fn [self x] (print "behaving 2 " x))})
|
||||
|
||||
(def thing1 (table/setproto @{} proto1))
|
||||
(def thing2 (table/setproto @{} proto2))
|
||||
|
||||
(print thing1:type) # prints :custom1
|
||||
(print thing2:type) # prints :custom2
|
||||
|
||||
(thing1:behave thing1 :a) # prints "behaving :a"
|
||||
(thing2:behave thing2 :b) # prints "behaving 2 :b"
|
||||
```
|
||||
|
||||
Looking up in a table with a prototype can be summed up with the following algorithm.
|
||||
|
||||
1. `(get my-table my-key)` is called.
|
||||
2. my-table is checked for the key if my-key. If there is a value for the key, it is returned.
|
||||
3. if there is a prototype table for my-table, set `my-table = my-table's prototype` and got to 2.
|
||||
4. Return nil as the key was not found.
|
||||
|
||||
Janet will check up to about a 1000 prototypes recursively by default before giving up and returning nil. This
|
||||
is to prevent an infinite loop. This value can be changed by adjusting the `JANET_RECURSION_GUARD` value
|
||||
in janet.h.
|
||||
|
||||
Note that Janet prototypes are not as expressive as metatables in Lua and many other languages.
|
||||
This is by design, as adding Lua or Python like capabilities would not be technically difficult.
|
||||
Users should prefer plain data and functions that operate on them rather than mutable objects
|
||||
with methods.
|
||||
|
||||
# Fibers
|
||||
|
||||
Janet has support for single-core asynchronous programming via coroutines, or fibers.
|
||||
Fibers allow a process to stop and resume execution later, essentially enabling
|
||||
multiple returns from a function. This allows many patterns such a schedules, generators,
|
||||
iterators, live debugging, and robust error handling. Janet's error handling is actually built on
|
||||
top of fibers (when an error is thrown, the parent fiber will handle the error).
|
||||
|
||||
A temporary return from a fiber is called a yield, and can be invoked with the `yield` function.
|
||||
To resume a fiber that has been yielded, use the `resume` function. When resume is called on a fiber,
|
||||
it will only return when that fiber either returns, yields, throws an error, or otherwise emits
|
||||
a signal.
|
||||
|
||||
Different from traditional coroutines, Janet's fibers implement a signaling mechanism, which
|
||||
is used to differentiate different kinds of returns. When a fiber yields or throws an error,
|
||||
control is returned to the calling fiber. The parent fiber must then check what kind of state the
|
||||
fiber is in to differentiate errors from return values from user defined signals.
|
||||
|
||||
To create a fiber, user the `fiber/new` function. The fiber constructor take one or two arguments.
|
||||
The first, necessary argument is the function that the fiber will execute. This function must accept
|
||||
an arity of zero. The next optional argument is a collection of flags checking what kinds of
|
||||
signals to trap and return via `resume`. This is useful so
|
||||
the programmer does not need to handle all different kinds of signals from a fiber. Any un-trapped signals
|
||||
are simply propagated to the next fiber.
|
||||
|
||||
```lisp
|
||||
(def f (fiber/new (fn []
|
||||
(yield 1)
|
||||
(yield 2)
|
||||
(yield 3)
|
||||
(yield 4)
|
||||
5)))
|
||||
|
||||
# Get the status of the fiber (:alive, :dead, :debug, :new, :pending, or :user0-:user9)
|
||||
(print (fiber/status f)) # -> :new
|
||||
|
||||
(print (resume f)) # -> prints 1
|
||||
(print (resume f)) # -> prints 2
|
||||
(print (resume f)) # -> prints 3
|
||||
(print (resume f)) # -> prints 4
|
||||
(print (fiber/status f)) # -> print :pending
|
||||
(print (resume f)) # -> prints 5
|
||||
(print (fiber/status f)) # -> print :dead
|
||||
(print (resume f)) # -> throws an error because the fiber is dead
|
||||
```
|
||||
|
||||
## Using Fibers to Capture Errors
|
||||
|
||||
Besides being used as coroutines, fibers can be used to implement error handling (exceptions).
|
||||
|
||||
```lisp
|
||||
(defn my-function-that-errors [x]
|
||||
(print "start function with " x)
|
||||
(error "oops!")
|
||||
(print "never gets here"))
|
||||
|
||||
# Use the :e flag to only trap errors.
|
||||
(def f (fiber/new my-function-that-errors :e))
|
||||
(def result (resume f))
|
||||
(if (= (fiber/status f) :error)
|
||||
(print "result contains the error")
|
||||
(print "result contains the good result"))
|
||||
```
|
||||
|
||||
# Macros
|
||||
|
||||
Janet supports macros like most lisps. A macro is like a function, but transforms
|
||||
the code itself rather than data. They let you extend the syntax of the language itself.
|
||||
|
||||
You have seen some macros already. The `let`, `loop`, and `defn` forms are macros. When the compiler
|
||||
sees a macro, it evaluates the macro and then compiles the result. We say the macro has been
|
||||
*expanded* after the compiler evaluates it. A simple version of the `defn` macro can
|
||||
be thought of as transforming code of the form
|
||||
|
||||
```lisp
|
||||
(defn1 myfun [x] body)
|
||||
```
|
||||
into
|
||||
```lisp
|
||||
(def myfun (fn myfun [x] body))
|
||||
```
|
||||
|
||||
We could write such a macro like so:
|
||||
|
||||
```lisp
|
||||
(defmacro defn1 [name args body]
|
||||
(tuple 'def name (tuple 'fn name args body)))
|
||||
```
|
||||
|
||||
There are a couple of issues with this macro, but it will work for simple functions
|
||||
quite well.
|
||||
|
||||
The first issue is that our defn2 macro can't define functions with multiple expressions
|
||||
in the body. We can make the macro variadic, just like a function. Here is a second version
|
||||
of this macro.
|
||||
|
||||
```lisp
|
||||
(defmacro defn2 [name args & body]
|
||||
(tuple 'def name (apply tuple 'fn name args body)))
|
||||
```
|
||||
|
||||
Great! Now we can define functions with multiple elements in the body. We can still improve this
|
||||
macro even more though. First, we can add a docstring to it. If someone is using the function later,
|
||||
they can use `(doc defn3)` to get a description of the function. Next, we can rewrite the macro
|
||||
using janet's builtin quasiquoting facilities.
|
||||
|
||||
```lisp
|
||||
(defmacro defn3
|
||||
"Defines a new function."
|
||||
[name args & body]
|
||||
~(def ,name (fn ,name ,args ,;body)))
|
||||
```
|
||||
|
||||
This is functionally identical to our previous version `defn2`, but written in such
|
||||
a way that the macro output is more clear. The leading tilde `~` is shorthand for the
|
||||
`(quasiquote x)` special form, which is like `(quote x)` except we can unquote
|
||||
expressions inside it. The comma in front of `name` and `args` is an unquote, which
|
||||
allows us to put a value in the quasiquote. Without the unquote, the symbol \'name\'
|
||||
would be put in the returned tuple. Without the unquote, every function we defined
|
||||
would be called \'name\'!.
|
||||
|
||||
Similar to name, we must also unquote body. However, a normal unquote doesn't work.
|
||||
See what happens if we use a normal unquote for body as well.
|
||||
|
||||
```lisp
|
||||
(def name 'myfunction)
|
||||
(def args '[x y z])
|
||||
(defn body '[(print x) (print y) (print z)])
|
||||
|
||||
~(def ,name (fn ,name ,args ,body))
|
||||
# -> (def myfunction (fn myfunction (x y z) ((print x) (print y) (print z))))
|
||||
```
|
||||
|
||||
There is an extra set of parentheses around the body of our function! We don't
|
||||
want to put the body *inside* the form `(fn args ...)`, we want to *splice* it
|
||||
into the form. Luckily, janet has the `(splice x)` special form for this purpose,
|
||||
and a shorthand for it, the ; character.
|
||||
When combined with the unquote special, we get the desired output.
|
||||
|
||||
```lisp
|
||||
~(def ,name (fn ,name ,args ,;body))
|
||||
# -> (def myfunction (fn myfunction (x y z) (print x) (print y) (print z)))
|
||||
```
|
||||
|
||||
## Hygiene
|
||||
|
||||
Sometime when we write macros, we must generate symbols for local bindings. Ignoring that
|
||||
it could be written as a function, consider
|
||||
the following macro
|
||||
|
||||
```lisp
|
||||
(defmacro max1
|
||||
"Get the max of two values."
|
||||
[x y]
|
||||
~(if (> ,x ,y) ,x ,y))
|
||||
```
|
||||
|
||||
This almost works, but will evaluate both x and y twice. This is because both show up
|
||||
in the macro twice. For example, `(max1 (do (print 1) 1) (do (print 2) 2))` will
|
||||
print both 1 and 2 twice, which is surprising to a user of this macro.
|
||||
|
||||
We can do better:
|
||||
|
||||
```lisp
|
||||
(defmacro max2
|
||||
"Get the max of two values."
|
||||
[x y]
|
||||
~(let [x ,x
|
||||
y ,y]
|
||||
(if (> x y) x y)))
|
||||
```
|
||||
|
||||
Now we have no double evaluation problem! But we now have an even more subtle problem.
|
||||
What happens in the following code?
|
||||
|
||||
```lisp
|
||||
(def x 10)
|
||||
(max2 8 (+ x 4))
|
||||
```
|
||||
|
||||
We want the max to be 14, but this will actually evaluate to 12! This can be understood
|
||||
if we expand the macro. You can expand macro once in janet using the `(macex1 x)` function.
|
||||
(To expand macros until there are no macros left to expand, use `(macex x)`. Be careful,
|
||||
janet has many macros, so the full expansion may be almost unreadable).
|
||||
|
||||
```lisp
|
||||
(macex1 '(max2 8 (+ x 4)))
|
||||
# -> (let (x 8 y (+ x 4)) (if (> x y) x y))
|
||||
```
|
||||
|
||||
After expansion, y wrongly refers to the x inside the macro (which is bound to 8) rather than the x defined
|
||||
to be 10. The problem is the reuse of the symbol x inside the macro, which overshadowed the original
|
||||
binding.
|
||||
|
||||
Janet provides a general solution to this problem in terms of the `(gensym)` function, which returns
|
||||
a symbol which is guaranteed to be unique and not collide with any symbols defined previously. We can define
|
||||
our macro once more for a fully correct macro.
|
||||
|
||||
```lisp
|
||||
(defmacro max3
|
||||
"Get the max of two values."
|
||||
[x y]
|
||||
(def $x (gensym))
|
||||
(def $y (gensym))
|
||||
~(let [,$x ,x
|
||||
,$y ,y]
|
||||
(if (> ,$x ,$y) ,$x ,$y)))
|
||||
```
|
||||
|
||||
As you can see, macros are very powerful but also are prone to subtle bugs. You must remember that
|
||||
at their core, macros are just functions that output code, and the code that they return must
|
||||
work in many contexts!
|
||||
174
doc/Loop.md
174
doc/Loop.md
@@ -1,174 +0,0 @@
|
||||
# Loops in Janet
|
||||
|
||||
A very common and essential operation in all programming is looping. Most
|
||||
languages support looping of some kind, either with explicit loops or recursion.
|
||||
Janet supports both recursion and a primitive `while` loop. While recursion is
|
||||
useful in many cases, sometimes is more convenient to use a explicit loop to
|
||||
iterate over a collection like an array.
|
||||
|
||||
## An Example - Iterating a Range
|
||||
|
||||
Suppose you want to calculate the sum of the first 10 natural numbers
|
||||
0 through 9. There are many ways to carry out this explicit calculation
|
||||
even with taking shortcuts. A succinct way in janet is
|
||||
|
||||
```
|
||||
(+ ;(range 10))
|
||||
```
|
||||
|
||||
We will limit ourselves however to using explicit looping and no functions
|
||||
like `(range n)` which generate a list of natural numbers for us.
|
||||
|
||||
For our first version, we will use only the while macro to iterate, similar
|
||||
to how one might sum natural numbers in a language such as C.
|
||||
|
||||
```
|
||||
(var sum 0)
|
||||
(var i 0)
|
||||
(while (< i 10)
|
||||
(+= sum i)
|
||||
(++ i))
|
||||
(print sum) # prints 45
|
||||
```
|
||||
This is a very imperative style program which can grow very large very quickly.
|
||||
We are manually updating a counter `i` in a loop. Using the macros `+=` and `++`, this
|
||||
style code is similar in density to C code.
|
||||
It is recommended to use either macros (such as the loop macro) or a functional
|
||||
style in janet.
|
||||
|
||||
Since this is such a common pattern, Janet has a macro for this exact purpose. The
|
||||
`(for x start end body)` captures exactly this behavior of incrementing a counter
|
||||
in a loop.
|
||||
|
||||
```
|
||||
(var sum 0)
|
||||
(for i 0 10 (+= sum i))
|
||||
(print sum) # prints 45
|
||||
```
|
||||
|
||||
We have completely wrapped the imperative counter in a macro. The for macro, while not
|
||||
very flexible, is very terse and covers a common case of iteration, iterating over an integer range. The for macro will be expanded to something very similar to our original
|
||||
version with a while loop.
|
||||
|
||||
We can do something similar with the more flexible `loop` macro.
|
||||
|
||||
```
|
||||
(var sum 0)
|
||||
(loop [i :range [0 10]] (+= sum i))
|
||||
(print sum) # prints 45
|
||||
```
|
||||
|
||||
This is slightly more verbose than the for macro, but can be more easily extended.
|
||||
Let's say that we wanted to only count even numbers towards the sum. We can do this
|
||||
easily with the loop macro.
|
||||
|
||||
```
|
||||
(var sum 0)
|
||||
(loop [i :range [0 10] :when (even? i)] (+= sum i))
|
||||
(print sum) # prints 20
|
||||
```
|
||||
|
||||
The loop macro has several verbs (:range) and modifiers (:when) that let
|
||||
the programmer more easily generate common looping idioms. The loop macro
|
||||
is similar to the Common Lips loop macro, but smaller in scope and with a much
|
||||
simpler syntax. As with the `for` macro, the loop macro expands to similar
|
||||
code as our original while expression.
|
||||
|
||||
## Another Example - Iterating an Indexed Data Structure
|
||||
|
||||
Another common usage for iteration in any language is iterating over the items in
|
||||
some data structure, like items in an array, characters in a string, or key value
|
||||
pairs in a table.
|
||||
|
||||
Say we have an array of names that we want to print out. We will
|
||||
again start with a simple while loop which we will refine into
|
||||
more idiomatic expressions.
|
||||
|
||||
First, we will define our array of names
|
||||
```
|
||||
(def names @["Jean-Paul Sartre" "Bob Dylan" "Augusta Ada King" "Frida Kahlo" "Harriet Tubman")
|
||||
```
|
||||
|
||||
With our array of names, we can use a while loop to iterate through the indices of names, get the
|
||||
values, and the print them.
|
||||
|
||||
```
|
||||
(var i 0)
|
||||
(def len (length names))
|
||||
(while (< i len)
|
||||
(print (get names i))
|
||||
(++ i))
|
||||
```
|
||||
|
||||
This is rather verbose. janet provides the `each` macro for iterating through the items in a tuple or
|
||||
array, or the bytes in a buffer, symbol, or string.
|
||||
|
||||
```
|
||||
(each name names (print name))
|
||||
```
|
||||
|
||||
We can also use the `loop` macro for this case as well using the `:in` verb.
|
||||
|
||||
```
|
||||
(loop [name :in names] (print name))
|
||||
```
|
||||
|
||||
## Iterating a Dictionary
|
||||
|
||||
In the previous example, we iterated over the values in an array. Another common
|
||||
use of looping in a Janet program is iterating over the keys or values in a table.
|
||||
We cannot use the same method as iterating over an array because a table or struct does
|
||||
not contain a known integer range of keys. Instead we rely on a function `next`, which allows
|
||||
us to visit each of the keys in a struct or table. Note that iterating over a table will not
|
||||
visit the prototype table.
|
||||
|
||||
As an example, lets iterate over a table of letters to a word that starts with that letter. We
|
||||
will print out the words to our simple children's book.
|
||||
|
||||
```
|
||||
(def alphabook
|
||||
@{"A" "Apple"
|
||||
"B" "Banana"
|
||||
"C" "Cat"
|
||||
"D" "Dog"
|
||||
"E" "Elephant" })
|
||||
```
|
||||
|
||||
As before, we can evaluate this loop using only a while loop and the `next` function.
|
||||
|
||||
```
|
||||
(var key (next alphabook nil))
|
||||
(while (not= nil key)
|
||||
(print key " is for " (get alphabook key))
|
||||
(set key (next alphabook key))
|
||||
```
|
||||
|
||||
However, we can do better than this with the loop macro using the `:pairs` or `:keys` verbs.
|
||||
|
||||
```
|
||||
(loop [[letter word] :pairs alphabook]
|
||||
(print letter " is for " word))
|
||||
```
|
||||
|
||||
Using the `:keys` verb and the dot syntax for indexing
|
||||
|
||||
```
|
||||
(loop [letter :keys alphabook]
|
||||
(print letter " is for " alphabook.letter))
|
||||
```
|
||||
|
||||
The symbol `alphabook.letter` is shorthand for `(get alphabook letter)`.
|
||||
Note that the dot syntax of `alphabook.letter` is different than in many languages. In C or
|
||||
ALGOL like languages, it is more akin to the indexing operator, and would be written `alphabook[letter]`.
|
||||
The `.` character is part of the symbol and is recognized by the compiler.
|
||||
|
||||
We can also use the core library functions `keys` and `pairs` to get arrays of the keys and
|
||||
pairs respectively of the alphabook.
|
||||
|
||||
```
|
||||
(loop [[letter word] :in (pairs alphabook)]
|
||||
(print letter " is for " word))
|
||||
|
||||
(loop [letter :in (keys alphabook)]
|
||||
(print letter " is for " alphabook.letter))
|
||||
```
|
||||
244
doc/Parser.md
244
doc/Parser.md
@@ -1,244 +0,0 @@
|
||||
# The Parser
|
||||
|
||||
A Janet program begins life as a text file, just a sequence of byte like
|
||||
any other on your system. Janet source files should be UTF-8 or ASCII
|
||||
encoded. Before Janet can compile or run your program, it must transform
|
||||
your source code into a data structure. Janet is a lisp, which means it is
|
||||
homoiconic - code is data, so all of the facilities in the language for
|
||||
manipulating arrays, tuples, strings, and tables can be used for manipulating
|
||||
your source code as well.
|
||||
|
||||
But before janet code is represented as a data structure, it must be read, or parsed,
|
||||
by the janet parser. Called the reader in many other lisps, the parser is a machine
|
||||
that takes in plain text and outputs data structures which can be used by both
|
||||
the compiler and macros. In janet, it is a parser rather than a reader because
|
||||
there is no code execution at read time. This is safer and simpler, and also
|
||||
lets janet syntax serve as a robust data interchange format. While a parser
|
||||
is not extensible, in janet the philosophy is to extend the language via macros
|
||||
rather than reader macros.
|
||||
|
||||
## Nil, True and False
|
||||
|
||||
Nil, true and false are all literals than can be entered as such
|
||||
in the parser.
|
||||
|
||||
```
|
||||
nil
|
||||
true
|
||||
false
|
||||
```
|
||||
|
||||
## Symbols
|
||||
|
||||
Janet symbols are represented a sequence of alphanumeric characters
|
||||
not starting with a digit or a colon. They can also contain the characters
|
||||
\!, @, $, \%, \^, \&, \*, -, \_, +, =, \|, \~, :, \<, \>, ., \?, \\, /, as
|
||||
well as any Unicode codepoint not in the ASCII range.
|
||||
|
||||
By convention, most symbols should be all lower case and use dashes to connect words
|
||||
(sometimes called kebab case).
|
||||
|
||||
Symbols that come from another module often contain a forward slash that separates
|
||||
the name of the module from the name of the definition in the module
|
||||
|
||||
```
|
||||
symbol
|
||||
kebab-case-symbol
|
||||
snake_case_symbol
|
||||
my-module/my-fuction
|
||||
*****
|
||||
!%$^*__--__._+++===~-crazy-symbol
|
||||
*global-var*
|
||||
你好
|
||||
```
|
||||
|
||||
## Keywords
|
||||
|
||||
Janet keywords are like symbols that begin with the character :. However, they
|
||||
are used differently and treated by the compiler as a constant rather than a name for
|
||||
something. Keywords are used mostly for keys in tables and structs, or pieces of syntax
|
||||
in macros.
|
||||
|
||||
```
|
||||
:keyword
|
||||
:range
|
||||
:0x0x0x0
|
||||
:a-keyword
|
||||
::
|
||||
:
|
||||
```
|
||||
|
||||
## Numbers
|
||||
|
||||
Janet numbers are represented by IEEE-754 floating point numbers.
|
||||
The syntax is similar to that of many other languages
|
||||
as well. Numbers can be written in base 10, with
|
||||
underscores used to separate digits into groups. A decimal point can be used for floating
|
||||
point numbers. Numbers can also be written in other bases by prefixing the number with the desired
|
||||
base and the character 'r'. For example, 16 can be written as `16`, `1_6`, `16r10`, `4r100`, or `0x10`. The
|
||||
`0x` prefix can be used for hexadecimal as it is so common. The radix must be themselves written in base 10, and
|
||||
can be any integer from 2 to 36. For any radix above 10, use the letters as digits (not case sensitive).
|
||||
|
||||
```
|
||||
0
|
||||
12
|
||||
-65912
|
||||
4.98
|
||||
1.3e18
|
||||
1.3E18
|
||||
18r123C
|
||||
11raaa&a
|
||||
1_000_000
|
||||
0xbeef
|
||||
```
|
||||
|
||||
## Strings
|
||||
|
||||
Strings in janet are surrounded by double quotes. Strings are 8bit clean, meaning
|
||||
meaning they can contain any arbitrary sequence of bytes, including embedded
|
||||
0s. To insert a double quote into a string itself, escape
|
||||
the double quote with a backslash. For unprintable characters, you can either use
|
||||
one of a few common escapes, use the `\xHH` escape to escape a single byte in
|
||||
hexidecimal. The supported escapes are:
|
||||
|
||||
- \\xHH Escape a single arbitrary byte in hexidecimal.
|
||||
- \\n Newline (ASCII 10)
|
||||
- \\t Tab character (ASCII 9)
|
||||
- \\r Carriage Return (ASCII 13)
|
||||
- \\0 Null (ASCII 0)
|
||||
- \\z Null (ASCII 0)
|
||||
- \\f Form Feed (ASCII 12)
|
||||
- \\e Escape (ASCII 27)
|
||||
- \\" Double Quote (ASCII 34)
|
||||
- \\\\ Backslash (ASCII 92)
|
||||
|
||||
Strings can also contain literal newline characters that will be ignore.
|
||||
This lets one define a multiline string that does not contain newline characters.
|
||||
|
||||
An alternative way of representing strings in janet is the long string, or the backquote
|
||||
delimited string. A string can also be define to start with a certain number of
|
||||
backquotes, and will end the same number of backquotes. Long strings
|
||||
do not contain escape sequences; all bytes will be parsed literally until
|
||||
ending delimiter is found. This is useful
|
||||
for defining multi-line strings with literal newline characters, unprintable
|
||||
characters, or strings that would otherwise require many escape sequences.
|
||||
|
||||
```
|
||||
"This is a string."
|
||||
"This\nis\na\nstring."
|
||||
"This
|
||||
is
|
||||
a
|
||||
string."
|
||||
``
|
||||
This
|
||||
is
|
||||
a
|
||||
string
|
||||
``
|
||||
```
|
||||
|
||||
## Buffers
|
||||
|
||||
Buffers are similar strings except they are mutable data structures. Strings in janet
|
||||
cannot be mutated after created, where a buffer can be changed after creation.
|
||||
The syntax for a buffer is the same as that for a string or long string, but
|
||||
the buffer must be prefixed with the '@' character.
|
||||
|
||||
```
|
||||
@""
|
||||
@"Buffer."
|
||||
@``Another buffer``
|
||||
```
|
||||
|
||||
## Tuples
|
||||
|
||||
Tuples are a sequence of white space separated values surrounded by either parentheses
|
||||
or brackets. The parser considers any of the characters ASCII 32, \\0, \\f, \\n, \\r or \\t
|
||||
to be white-space.
|
||||
|
||||
```
|
||||
(do 1 2 3)
|
||||
[do 1 2 3]
|
||||
```
|
||||
|
||||
## Arrays
|
||||
|
||||
Arrays are the same as tuples, but have a leading @ to indicate mutability.
|
||||
|
||||
```
|
||||
@(:one :two :three)
|
||||
@[:one :two :three]
|
||||
```
|
||||
|
||||
## Structs
|
||||
|
||||
Structs are represented by a sequence of white-space delimited key value pairs
|
||||
surrounded by curly braces. The sequence is defined as key1, value1, key2, value2, etc.
|
||||
There must be an even number of items between curly braces or the parser will
|
||||
signal a parse error. Any value can be a key or value. Using nil as a key or
|
||||
value, however, will drop that pair from the parsed struct.
|
||||
|
||||
```
|
||||
{}
|
||||
{:key1 "value1" :key2 :value2 :key3 3}
|
||||
{(1 2 3) (4 5 6)}
|
||||
{@[] @[]}
|
||||
{1 2 3 4 5 6}
|
||||
```
|
||||
## Tables
|
||||
|
||||
Table have the same syntax as structs, except they have the @ prefix to indicate
|
||||
that they are mutable.
|
||||
|
||||
```
|
||||
@{}
|
||||
@{:key1 "value1" :key2 :value2 :key3 3}
|
||||
@{(1 2 3) (4 5 6)}
|
||||
@{@[] @[]}
|
||||
@{1 2 3 4 5 6}
|
||||
```
|
||||
|
||||
## Comments
|
||||
|
||||
Comments begin with a \# character and continue until the end of the line.
|
||||
There are no multi-line comments.
|
||||
|
||||
## Shorthand
|
||||
|
||||
Often called reader macros in other lisps, Janet provides several shorthand
|
||||
notations for some forms.
|
||||
|
||||
### 'x
|
||||
|
||||
Shorthand for `(quote x)`
|
||||
|
||||
### ;x
|
||||
|
||||
Shorthand for `(splice x)`
|
||||
|
||||
### ~x
|
||||
|
||||
Shorthand for `(quasiquote x)`
|
||||
|
||||
### ,x
|
||||
|
||||
Shorthand for `(unquote x)`
|
||||
|
||||
These shorthand notations can be combined in any order, allowing
|
||||
forms like `''x` (`(quote (quote x))`), or `,;x` (`(unquote (splice x))`).
|
||||
|
||||
## API
|
||||
|
||||
The parser contains the following functions which exposes
|
||||
the parser state machine as a janet abstract object.
|
||||
|
||||
- `parser/byte`
|
||||
- `parser/consume`
|
||||
- `parser/error`
|
||||
- `parser/flush`
|
||||
- `parser/new`
|
||||
- `parser/produce`
|
||||
- `parser/state`
|
||||
- `parser/status`
|
||||
- `parser/where`
|
||||
206
doc/Specials.md
206
doc/Specials.md
@@ -1,206 +0,0 @@
|
||||
# Special Forms
|
||||
|
||||
Janet is a lisp and so is defined in terms of mostly S-expressions, or
|
||||
in terms of Janet, tuples. Tuples are used to represent function calls, macros,
|
||||
and special forms. Most functionality is exposed through functions, some
|
||||
through macros, and a minimal amount through special forms. Special forms
|
||||
are neither functions nor macros -- they are used by the compiler to directly
|
||||
express a low level construct that can not be expressed through macros or functions.
|
||||
Special forms can be thought of as forming the real 'core' language of janet.
|
||||
|
||||
Below is a reference for all of the special forms in Janet.
|
||||
|
||||
## (def name meta... value)
|
||||
|
||||
This special form binds a value to a symbol. The symbol can the be substituted
|
||||
for the value in subsequent expression for the same result. A binding made by def
|
||||
is a constant and cannot be updated. A symbol can be redefined to a new value, but previous
|
||||
uses of the binding will refer to the previous value of the binding.
|
||||
|
||||
```lisp
|
||||
(def anumber (+ 1 2 3 4 5))
|
||||
|
||||
(print anumber) # prints 15
|
||||
```
|
||||
|
||||
Def can also take a tuple, array, table or struct to perform destructuring
|
||||
on the value. This allows us to do multiple assignments in one def.
|
||||
|
||||
```lisp
|
||||
(def [a b c] (range 10))
|
||||
(print a " " b " " c) # prints 0 1 2
|
||||
|
||||
(def {:x x} @{:x (+ 1 2)})
|
||||
(print x) # prints 3
|
||||
|
||||
(def [y {:x x}] @[:hi @{:x (+ 1 2)}])
|
||||
(print y x) # prints hi3
|
||||
```
|
||||
|
||||
Def can also append metadata and a docstring to the symbol when in the global scope.
|
||||
If not in the global scope, the extra metadata will be ignored.
|
||||
|
||||
```lisp
|
||||
(def mydef :private 3) # Adds the :private key to the metadata table.
|
||||
(def mydef2 :private "A docstring" 4) # Add a docstring
|
||||
|
||||
# The metadata will be ignored here because mydef is
|
||||
# accessible outside of the do form.
|
||||
(do
|
||||
(def mydef :private 3)
|
||||
(+ mydef 1))
|
||||
```
|
||||
|
||||
## (var name meta... value)
|
||||
|
||||
Similar to def, but bindings set in this manner can be updated using set. In all other respects is the
|
||||
same as def.
|
||||
|
||||
```lisp
|
||||
(var a 1)
|
||||
(defn printa [] (print a))
|
||||
|
||||
(printa) # prints 1
|
||||
(++ a)
|
||||
(printa) # prints 2
|
||||
(set a :hi)
|
||||
(printa) # prints hi
|
||||
```
|
||||
|
||||
## (fn name? args body...)
|
||||
|
||||
Compile a function literal (closure). A function literal consists of an optional name, an
|
||||
argument list, and a function body. The optional name is allowed so that functions can
|
||||
more easily be recursive. The argument list is a tuple of named parameters, and the body
|
||||
is 0 or more forms. The function will evaluate to the last form in the body. The other forms
|
||||
will only be evaluated for side effects.
|
||||
|
||||
Functions also introduced a new lexical scope, meaning the defs and vars inside a function
|
||||
body will not escape outside the body.
|
||||
|
||||
```lisp
|
||||
(fn []) # The simplest function literal. Takes no arguments and returns nil.
|
||||
(fn [x] x) # The identity function
|
||||
(fn identity [x] x) # The identity function - the name will also make stacktraces nicer.
|
||||
(fn [] 1 2 3 4 5) # A function that returns 5
|
||||
(fn [x y] (+ x y)) # A function that adds its two arguments.
|
||||
|
||||
(fn [& args] (length args)) # A variadic function that counts its arguments.
|
||||
|
||||
# A function that doesn't strictly check the number of arguments.
|
||||
# Extra arguments are ignored, and arguments not passed are nil.
|
||||
(fn [w x y z &] (tuple w w x x y y z z))
|
||||
```
|
||||
|
||||
## (do body...)
|
||||
|
||||
Execute a series of forms for side effects and evaluates to the final form. Also
|
||||
introduces a new lexical scope without creating or calling a function.
|
||||
|
||||
```lisp
|
||||
(do 1 2 3 4) # Evaluates to 4
|
||||
|
||||
# Prints 1, 2 and 3, then evaluates to (print 3), which is nil
|
||||
(do (print 1) (print 2) (print 3))
|
||||
|
||||
# Prints 1
|
||||
(do
|
||||
(def a 1)
|
||||
(print a))
|
||||
|
||||
# a is not defined here, so fails
|
||||
a
|
||||
```
|
||||
|
||||
## (quote x)
|
||||
|
||||
Evaluates to the literal value of the first argument. The argument is not compiled
|
||||
and is simply used as a constant value in the compiled code. Preceding a form with a
|
||||
single quote is shorthand for `(quote expression)`.
|
||||
|
||||
```lisp
|
||||
(quote 1) # evaluates to 1
|
||||
(quote hi) # evaluates to the symbol hi
|
||||
(quote quote) # evaluates to the symbol quote
|
||||
|
||||
`(1 2 3) # Evaluates to a tuple (1 2 3)
|
||||
`(print 1 2 3) # Evaluates to a tuple (print 1 2 3)
|
||||
```
|
||||
|
||||
## (if condition when-true when-false?)
|
||||
|
||||
Introduce a branching construct. The first form is the condition, the second
|
||||
form is the form to evaluate when the condition is true, and the optional
|
||||
third form is the form to evaluate when the condition is false. If no third
|
||||
form is provided it defaults to nil.
|
||||
|
||||
The if special form will not evaluate the when-true or when-false forms unless
|
||||
it needs to - it is a lazy form, which is why it cannot be a function or macro.
|
||||
|
||||
The condition is considered false only if it evaluates to nil or false - all other values
|
||||
are considered true.
|
||||
|
||||
```lisp
|
||||
(if true 10) # evaluates to 10
|
||||
(if false 10) # evaluates to nil
|
||||
(if true (print 1) (print 2)) # prints 1 but not 2
|
||||
```
|
||||
|
||||
## (splice x)
|
||||
|
||||
The splice special form is an interesting form that doesn't have an analog in most lisps.
|
||||
It only has an effect in two places - as an argument in a function call, or as the argument
|
||||
to the unquote form. Outside of these two settings, the splice special form simply evaluates
|
||||
directly to it's argument x. The shorthand for splice is prefixing a form with a semicolon.
|
||||
|
||||
In the context of a function call, splice will insert *the contents* of x in the parameter list.
|
||||
|
||||
```lisp
|
||||
(+ 1 2 3) # evaluates to 6
|
||||
|
||||
(+ @[1 2 3]) # bad
|
||||
|
||||
(+ (splice @[1 2 3])) # also evaluates to 6
|
||||
|
||||
(+ ;@[1 2 3]) # Same as above
|
||||
|
||||
(+ ;(range 100)) # Sum the first 100 natural numbers
|
||||
|
||||
(+ ;(range 100) 1000) # Sum the first 100 natural numbers and 1000
|
||||
```
|
||||
|
||||
Notice that this means we rarely will need the `apply` function, as the splice operator is more flexible.
|
||||
|
||||
The splice operator can also be used inside an unquote form, where it will behave like
|
||||
an `unquote-splicing` special in other lisps.
|
||||
|
||||
## (while condition body...)
|
||||
|
||||
The while special form compiles to a C-like while loop. The body of the form will be continuously evaluated
|
||||
until the condition is false or nil. Therefor, it is expected that the body will contain some side effects
|
||||
of the loop will go on for ever. The while loop always evaluates to nil.
|
||||
|
||||
```lisp
|
||||
(var i 0)
|
||||
(while (< i 10)
|
||||
(print i)
|
||||
(++ i))
|
||||
```
|
||||
|
||||
## (set l-value r-value)
|
||||
|
||||
Update the value of a var l-value to a new value r-value. The set special form will then evaluate to r-value.
|
||||
|
||||
The r-value can be any expression, and the l-value should be a bound var.
|
||||
|
||||
## (quasiquote x)
|
||||
|
||||
Similar to `(quote x)`, but allows for unquoting within x. This makes quasiquote useful for
|
||||
writing macros, as a macro definition often generates a lot of templated code with a
|
||||
few custom values. The shorthand for quasiquote is a leading tilde `~` before a form. With
|
||||
that form, `(unquote x)` will evaluate and insert x into the unquote form. The shorthand for
|
||||
`(unquote x)` is `,x`.
|
||||
|
||||
## (unquote x)
|
||||
|
||||
Unquote a form within a quasiquote. Outside of a quasiquote, unquote is invalid.
|
||||
@@ -1,224 +0,0 @@
|
||||
The Janet language is implemented on top of an abstract machine (AM). The compiler
|
||||
converts Janet data structures to this bytecode, which can then be efficiently executed
|
||||
from inside a C program. To understand the janet bytecode, it is useful to understand
|
||||
the abstractions used inside the Janet AM, as well as the C types used to implement these
|
||||
features.
|
||||
|
||||
## The Stack = The Fiber
|
||||
|
||||
A Janet Fiber is the type used to represent multiple concurrent processes
|
||||
in janet. It is basically a wrapper around the idea of a stack. The stack is
|
||||
divided into a number of stack frames (`JanetStackFrame *` in C), each of which
|
||||
contains information such as the function that created the stack frame,
|
||||
the program counter for the stack frame, a pointer to the previous frame,
|
||||
and the size of the frame. Each stack frame also is paired with a number
|
||||
registers.
|
||||
|
||||
```
|
||||
X: Slot
|
||||
|
||||
X
|
||||
X - Stack Top, for next function call.
|
||||
-----
|
||||
Frame next
|
||||
-----
|
||||
X
|
||||
X
|
||||
X
|
||||
X
|
||||
X
|
||||
X
|
||||
X - Stack 0
|
||||
-----
|
||||
Frame 0
|
||||
-----
|
||||
X
|
||||
X
|
||||
X - Stack -1
|
||||
-----
|
||||
Frame -1
|
||||
-----
|
||||
X
|
||||
X
|
||||
X
|
||||
X
|
||||
X - Stack -2
|
||||
-----
|
||||
Frame -2
|
||||
-----
|
||||
...
|
||||
...
|
||||
...
|
||||
-----
|
||||
Bottom of stack
|
||||
```
|
||||
|
||||
Fibers also have an incomplete stack frame for the next function call on top
|
||||
of their stacks. Making a function call involves pushing arguments to this
|
||||
temporary stack, and then invoking either the CALL or TCALL instructions.
|
||||
Arguments for the next function call are pushed via the PUSH, PUSH2, PUSH3, and
|
||||
PUSHA instructions. The stack of a fiber will grow as large as needed, although by
|
||||
default janet will limit the maximum size of a fiber's stack.
|
||||
The maximum stack size can be modified on a per fiber basis.
|
||||
|
||||
The slots in the stack are exposed as virtual registers to instructions. They
|
||||
can hold any Janet value.
|
||||
|
||||
## Closures
|
||||
|
||||
All functions in janet are closures; they combine some bytecode instructions
|
||||
with 0 or more environments. In the C source, a closure (hereby the same as
|
||||
a function) is represented by the type `JanetFunction *`. The bytecode instruction
|
||||
part of the function is represented by `JanetFuncDef *`, and a function environment
|
||||
is represented with `JanetFuncEnv *`.
|
||||
|
||||
The function definition part of a function (the 'bytecode' part, `JanetFuncDef *`),
|
||||
we also store various metadata about the function which is useful for debugging,
|
||||
as well as constants referenced by the function.
|
||||
|
||||
## C Functions
|
||||
|
||||
Janet uses C functions to bridge to native code. A C function
|
||||
(`JanetCFunction *` in C) is a C function pointer that can be called like
|
||||
a normal janet closure. From the perspective of the bytecode instruction set, there is no difference
|
||||
in invoking a C function and invoking a normal janet function.
|
||||
|
||||
## Bytecode Format
|
||||
|
||||
Janet bytecode presents an interface to a virtual machine with a large number
|
||||
of identical registers that can hold any Janet value (`Janet *` in C). Most instructions
|
||||
have a destination register, and 1 or 2 source register. Registers are simply
|
||||
named with positive integers.
|
||||
|
||||
Each instruction is a 32 bit integer, meaning that the instruction set is a constant
|
||||
width RISC instruction set like MIPS. The opcode of each instruction is the least significant
|
||||
byte of the instruction. The highest bit of
|
||||
this leading byte is reserved for debugging purpose, so there are 128 possible opcodes encodable
|
||||
with this scheme. Not all of these possible opcode are defined, and will trap the interpreter
|
||||
and emit a debug signal. Note that this mean an unknown opcode is still valid bytecode, it will
|
||||
just put the interpreter into a debug state when executed.
|
||||
|
||||
```
|
||||
X - Payload bits
|
||||
O - Opcode bits
|
||||
|
||||
4 3 2 1
|
||||
+----+----+----+----+
|
||||
| XX | XX | XX | OO |
|
||||
+----+----+----+----+
|
||||
```
|
||||
|
||||
8 bits for the opcode leaves 24 bits for the payload, which may or may not be utilized.
|
||||
There are a few instruction variants that divide these payload bits.
|
||||
|
||||
* 0 arg - Used for noops, returning nil, or other instructions that take no
|
||||
arguments. The payload is essentially ignored.
|
||||
* 1 arg - All payload bits correspond to a single value, usually a signed or unsigned integer.
|
||||
Used for instructions of 1 argument, like returning a value, yielding a value to the parent fiber,
|
||||
or doing a (relative) jump.
|
||||
* 2 arg - Payload is split into byte 2 and bytes 3 and 4.
|
||||
The first argument is the 8 bit value from byte 2, and the second argument is the 16 bit value
|
||||
from bytes 3 and 4 (`instruction >> 16`). Used for instructions of two arguments, like move, normal
|
||||
function calls, conditionals, etc.
|
||||
* 3 arg - Bytes 2, 3, and 4 each correspond to an 8 bit argument.
|
||||
Used for arithmetic operations, emitting a signal, etc.
|
||||
|
||||
These instruction variants can be further refined based on the semantics of the arguments.
|
||||
Some instructions may treat an argument as a slot index, while other instructions
|
||||
will treat the argument as a signed integer literal, and index for a constant, an index
|
||||
for an environment, or an unsigned integer.
|
||||
|
||||
## Instruction Reference
|
||||
|
||||
A listing of all opcode values can be found in src/include/janet/janetopcodes.h. The janet assembly
|
||||
short names can be found src/assembler/asm.c. In this document, we will refer to the instructions
|
||||
by their short names as presented to the assembler rather than their numerical values.
|
||||
|
||||
Each instruction is also listed with a signature, which are the arguments the instruction
|
||||
expects. There are a handful of instruction signatures, which combine the arity and type
|
||||
of the instruction. The assembler does not
|
||||
do any type-checking per closure, but does prevent jumping to invalid instructions and
|
||||
failure to return or error.
|
||||
|
||||
### Notation
|
||||
|
||||
* The $ prefix indicates that a instruction parameter is acting as a virtual register (slot).
|
||||
If a parameter does not have the $ suffix in the description, it is acting as some kind
|
||||
of literal (usually an unsigned integer for indexes, and a signed integer for literal integers).
|
||||
|
||||
* Some operators in the description have the suffix 'i' or 'r'. These indicate
|
||||
that these operators correspond to integers or real numbers only, respectively. All
|
||||
bit-wise operators and bit shifts only work with integers.
|
||||
|
||||
* The `>>>` indicates unsigned right shift, as in Java. Because all integers in janet are
|
||||
signed, we differentiate the two kinds of right bit shift.
|
||||
|
||||
* The 'im' suffix in the instruction name is short for immediate.
|
||||
|
||||
### Reference Table
|
||||
|
||||
| Instruction | Signature | Description |
|
||||
| ----------- | --------------------------- | --------------------------------- |
|
||||
| `add` | `(add dest lhs rhs)` | $dest = $lhs + $rhs |
|
||||
| `addim` | `(addim dest lhs im)` | $dest = $lhs + im |
|
||||
| `band` | `(band dest lhs rhs)` | $dest = $lhs & $rhs |
|
||||
| `bnot` | `(bnot dest operand)` | $dest = ~$operand |
|
||||
| `bor` | `(bor dest lhs rhs)` | $dest = $lhs | $rhs |
|
||||
| `bxor` | `(bxor dest lhs rhs)` | $dest = $lhs ^ $rhs |
|
||||
| `call` | `(call dest callee)` | $dest = call($callee, args) |
|
||||
| `clo` | `(clo dest index)` | $dest = closure(defs[$index]) |
|
||||
| `cmp` | `(cmp dest lhs rhs)` | $dest = janet\_compare($lhs, $rhs)|
|
||||
| `div` | `(div dest lhs rhs)` | $dest = $lhs / $rhs |
|
||||
| `divim` | `(divim dest lhs im)` | $dest = $lhs / im |
|
||||
| `eq` | `(eq dest lhs rhs)` | $dest = $lhs == $rhs |
|
||||
| `eqim` | `(eqim dest lhs im)` | $dest = $lhs == im |
|
||||
| `err` | `(err message)` | Throw error $message. |
|
||||
| `get` | `(get dest ds key)` | $dest = $ds[$key] |
|
||||
| `geti` | `(geti dest ds index)` | $dest = $ds[index] |
|
||||
| `gt` | `(gt dest lhs rhs)` | $dest = $lhs \> $rhs |
|
||||
| `gtim` | `(gtim dest lhs im)` | $dest = $lhs \> im |
|
||||
| `jmp` | `(jmp label)` | pc = label, pc += offset |
|
||||
| `jmpif` | `(jmpif cond label)` | if $cond pc = label else pc++ |
|
||||
| `jmpno` | `(jmpno cond label)` | if $cond pc++ else pc = label |
|
||||
| `ldc` | `(ldc dest index)` | $dest = constants[index] |
|
||||
| `ldf` | `(ldf dest)` | $dest = false |
|
||||
| `ldi` | `(ldi dest integer)` | $dest = integer |
|
||||
| `ldn` | `(ldn dest)` | $dest = nil |
|
||||
| `lds` | `(lds dest)` | $dest = current closure (self) |
|
||||
| `ldt` | `(ldt dest)` | $dest = true |
|
||||
| `ldu` | `(ldu dest env index)` | $dest = envs[env][index] |
|
||||
| `len` | `(len dest ds)` | $dest = length(ds) |
|
||||
| `lt` | `(lt dest lhs rhs)` | $dest = $lhs \< $rhs |
|
||||
| `ltim` | `(ltim dest lhs im)` | $dest = $lhs \< im |
|
||||
| `mkarr` | `(mkarr dest)` | $dest = call(array, args) |
|
||||
| `mkbuf` | `(mkbuf dest)` | $dest = call(buffer, args) |
|
||||
| `mktab` | `(mktab dest)` | $dest = call(table, args) |
|
||||
| `mkstr` | `(mkstr dest)` | $dest = call(string, args) |
|
||||
| `mkstu` | `(mkstu dest)` | $dest = call(struct, args) |
|
||||
| `mktup` | `(mktup dest)` | $dest = call(tuple, args) |
|
||||
| `movf` | `(movf src dest)` | $dest = $src |
|
||||
| `movn` | `(movn dest src)` | $dest = $src |
|
||||
| `mul` | `(mul dest lhs rhs)` | $dest = $lhs \* $rhs |
|
||||
| `mulim` | `(mulim dest lhs im)` | $dest = $lhs \* im |
|
||||
| `noop` | `(noop)` | Does nothing. |
|
||||
| `push` | `(push val)` | Push $val on arg |
|
||||
| `push2` | `(push2 val1 val3)` | Push $val1, $val2 on args |
|
||||
| `push3` | `(push3 val1 val2 val3)` | Push $val1, $val2, $val3, on args |
|
||||
| `pusha` | `(pusha array)` | Push values in $array on args |
|
||||
| `put` | `(put ds key val)` | $ds[$key] = $val |
|
||||
| `puti` | `(puti ds index val)` | $ds[index] = $val |
|
||||
| `res` | `(res dest fiber val)` | $dest = resume $fiber with $val |
|
||||
| `ret` | `(ret val)` | Return $val |
|
||||
| `retn` | `(retn)` | Return nil |
|
||||
| `setu` | `(setu env index val)` | envs[env][index] = $val |
|
||||
| `sig` | `(sig dest value sigtype)` | $dest = emit $value as sigtype |
|
||||
| `sl` | `(sl dest lhs rhs)` | $dest = $lhs << $rhs |
|
||||
| `slim` | `(slim dest lhs shamt)` | $dest = $lhs << shamt |
|
||||
| `sr` | `(sr dest lhs rhs)` | $dest = $lhs >> $rhs |
|
||||
| `srim` | `(srim dest lhs shamt)` | $dest = $lhs >> shamt |
|
||||
| `sru` | `(sru dest lhs rhs)` | $dest = $lhs >>> $rhs |
|
||||
| `sruim` | `(sruim dest lhs shamt)` | $dest = $lhs >>> shamt |
|
||||
| `sub` | `(sub dest lhs rhs)` | $dest = $lhs - $rhs |
|
||||
| `tcall` | `(tcall callee)` | Return call($callee, args) |
|
||||
| `tchck` | `(tcheck slot types)` | Assert $slot does matches types |
|
||||
|
||||
23
examples/numarray/build.janet
Normal file
23
examples/numarray/build.janet
Normal file
@@ -0,0 +1,23 @@
|
||||
(import cook)
|
||||
|
||||
(cook/make-native
|
||||
:name "numarray"
|
||||
:source @["numarray.c"])
|
||||
|
||||
(import build/numarray :prefix "")
|
||||
|
||||
(def a (numarray/new 30))
|
||||
(print (get a 20))
|
||||
(print (a 20))
|
||||
|
||||
(put a 5 3.14)
|
||||
(print (a 5))
|
||||
(set (a 5) 100)
|
||||
(print (a 5))
|
||||
|
||||
# (numarray/scale a 5))
|
||||
# ((a :scale) a 5)
|
||||
(:scale a 5)
|
||||
(for i 0 10 (print (a i)))
|
||||
|
||||
(print "sum=" (:sum a))
|
||||
115
examples/numarray/numarray.c
Normal file
115
examples/numarray/numarray.c
Normal file
@@ -0,0 +1,115 @@
|
||||
#include <stdlib.h>
|
||||
#include <janet/janet.h>
|
||||
|
||||
typedef struct {
|
||||
double * data;
|
||||
size_t size;
|
||||
} num_array;
|
||||
|
||||
static num_array * num_array_init(num_array * array,size_t size) {
|
||||
array->data=(double *)calloc(size,sizeof(double));
|
||||
array->size=size;
|
||||
return array;
|
||||
}
|
||||
|
||||
static void num_array_deinit(num_array * array) {
|
||||
free(array->data);
|
||||
}
|
||||
|
||||
static int num_array_gc(void *p, size_t s) {
|
||||
(void) s;
|
||||
num_array * array=(num_array *)p;
|
||||
num_array_deinit(array);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Janet num_array_get(void *p, Janet key);
|
||||
void num_array_put(void *p, Janet key, Janet value);
|
||||
|
||||
static const JanetAbstractType num_array_type = {
|
||||
"numarray",
|
||||
num_array_gc,
|
||||
NULL,
|
||||
num_array_get,
|
||||
num_array_put
|
||||
};
|
||||
|
||||
static Janet num_array_new(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
int32_t size=janet_getinteger(argv,0);
|
||||
num_array * array = (num_array *)janet_abstract(&num_array_type,sizeof(num_array));
|
||||
num_array_init(array,size);
|
||||
return janet_wrap_abstract(array);
|
||||
}
|
||||
|
||||
static Janet num_array_scale(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 2);
|
||||
num_array * array = (num_array *)janet_getabstract(argv,0,&num_array_type);
|
||||
double factor = janet_getnumber(argv,1);
|
||||
size_t i;
|
||||
for (i=0;i<array->size;i++) {
|
||||
array->data[i]*=factor;
|
||||
}
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static Janet num_array_sum(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
num_array * array = (num_array *)janet_getabstract(argv,0,&num_array_type);
|
||||
double sum = 0;
|
||||
for (size_t i=0;i<array->size;i++) sum+=array->data[i];
|
||||
return janet_wrap_number(sum);
|
||||
}
|
||||
|
||||
void num_array_put(void *p, Janet key, Janet value) {
|
||||
size_t index;
|
||||
num_array * array=(num_array *)p;
|
||||
if (!janet_checkint(key))
|
||||
janet_panic("expected integer key");
|
||||
if (!janet_checktype(value,JANET_NUMBER))
|
||||
janet_panic("expected number value");
|
||||
|
||||
index = (size_t)janet_unwrap_integer(key);
|
||||
if (index < array->size) {
|
||||
array->data[index]=janet_unwrap_number(value);
|
||||
}
|
||||
}
|
||||
|
||||
static const JanetMethod methods[] = {
|
||||
{"scale", num_array_scale},
|
||||
{"sum", num_array_sum},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
Janet num_array_get(void *p, Janet key) {
|
||||
size_t index;
|
||||
Janet value;
|
||||
num_array * array=(num_array *)p;
|
||||
if (janet_checktype(key, JANET_KEYWORD))
|
||||
return janet_getmethod(janet_unwrap_keyword(key), methods);
|
||||
if (!janet_checkint(key))
|
||||
janet_panic("expected integer key");
|
||||
index = (size_t)janet_unwrap_integer(key);
|
||||
if (index >= array->size) {
|
||||
value = janet_wrap_nil();
|
||||
} else {
|
||||
value = janet_wrap_number(array->data[index]);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static const JanetReg cfuns[] = {
|
||||
{"numarray/new", num_array_new,
|
||||
"(numarray/new size)\n\n"
|
||||
"Create new numarray"
|
||||
},
|
||||
{"numarray/scale", num_array_scale,
|
||||
"(numarray/scale numarray factor)\n\n"
|
||||
"scale numarray by factor"
|
||||
},
|
||||
{NULL,NULL,NULL}
|
||||
};
|
||||
|
||||
JANET_MODULE_ENTRY(JanetTable *env) {
|
||||
janet_cfuns(env, "numarray", cfuns);
|
||||
}
|
||||
41
janet.1
41
janet.1
@@ -1,12 +1,13 @@
|
||||
.TH JANET 1
|
||||
.SH NAME
|
||||
janet \- run the janet language abstract machine
|
||||
janet \- run the Janet language abstract machine
|
||||
.SH SYNOPSIS
|
||||
.B janet
|
||||
[\fB\-hvsrp\fR]
|
||||
[\fB\-e\fR \fIJANET SOURCE\fR]
|
||||
[\fB\-\-\fR]
|
||||
.IR files ...
|
||||
.IR script
|
||||
.IR args ...
|
||||
.SH DESCRIPTION
|
||||
Janet is a functional and imperative programming language and bytecode interpreter.
|
||||
It is a modern lisp, but lists are replaced by other data structures with better utility
|
||||
@@ -14,10 +15,10 @@ and performance (arrays, tables, structs, tuples). The language also bridging br
|
||||
to native code written in C, meta-programming with macros, and bytecode assembly.
|
||||
|
||||
There is a repl for trying out the language, as well as the ability to run script files.
|
||||
This client program is separate from the core runtime, so janet could be embedded
|
||||
into other programs. Try janet in your browser at https://janet-lang.org.
|
||||
This client program is separate from the core runtime, so Janet could be embedded
|
||||
into other programs. Try Janet in your browser at https://Janet-lang.org.
|
||||
|
||||
Implemented in mostly standard C99, janet runs on Windows, Linux and macOS.
|
||||
Implemented in mostly standard C99, Janet runs on Windows, Linux and macOS.
|
||||
The few features that are not standard C99 (dynamic library loading, compiler
|
||||
specific optimizations), are fairly straight forward. Janet can be easily ported to
|
||||
most new platforms.
|
||||
@@ -37,24 +38,34 @@ Shows the version text and exits immediately.
|
||||
|
||||
.TP
|
||||
.BR \-s
|
||||
Read raw input from stdin, such as from a pipe without printing a prompt.
|
||||
Read raw input from stdin and forgo prompt history and other readline-like features.
|
||||
|
||||
.TP
|
||||
.BR \-q
|
||||
Quiet output. Don't print a repl prompt or expression results to stdout.
|
||||
|
||||
.TP
|
||||
.BR \-r
|
||||
Open a REPL (Read Eval Print Loop) after executing all sources. By default, if janet is called with no
|
||||
Open a REPL (Read Eval Print Loop) after executing all sources. By default, if Janet is called with no
|
||||
arguments, a REPL is opened.
|
||||
|
||||
.TP
|
||||
.BR \-p
|
||||
Turn on the persistent flag. By default, when janet is executing commands from a file and encounters an error,
|
||||
it will immediately exit after printing the error message. In persistent mode, janet will keep executing commands
|
||||
Turn on the persistent flag. By default, when Janet is executing commands from a file and encounters an error,
|
||||
it will immediately exit after printing the error message. In persistent mode, Janet will keep executing commands
|
||||
after an error. Persistent mode can be good for debugging and testing.
|
||||
|
||||
.TP
|
||||
.BR \-e
|
||||
Execute a string of janet source. Source code is executed in the order it is encountered, so earlier
|
||||
Execute a string of Janet source. Source code is executed in the order it is encountered, so earlier
|
||||
arguments are executed before later ones.
|
||||
|
||||
.TP
|
||||
.BR \-l
|
||||
Load a Janet file before running a script or repl. Multiple files can be loaded
|
||||
in this manner, and exports from each file will be made available to the script
|
||||
or repl.
|
||||
|
||||
.TP
|
||||
.BR \-\-
|
||||
Stop parsing command line arguments. All arguments after this one will be considered file names.
|
||||
@@ -63,11 +74,11 @@ Stop parsing command line arguments. All arguments after this one will be consid
|
||||
|
||||
.B JANET_PATH
|
||||
.RS
|
||||
The location to look for janet libraries. This is the only environment variable janet needs to
|
||||
find native and source code modules. If no JANET_PATH is set, janet will look in
|
||||
/usr/local/lib/janet for modules.
|
||||
To make janet search multiple locations, modify the module.paths
|
||||
array in janet.
|
||||
The location to look for Janet libraries. This is the only environment variable Janet needs to
|
||||
find native and source code modules. If no JANET_PATH is set, Janet will look in
|
||||
/usr/local/lib/Janet for modules.
|
||||
To make Janet search multiple locations, modify the module.paths
|
||||
array in Janet.
|
||||
.RE
|
||||
|
||||
.SH AUTHOR
|
||||
|
||||
43
src/boot/boot.c
Normal file
43
src/boot/boot.c
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <janet/janet.h>
|
||||
|
||||
extern const unsigned char *janet_gen_boot;
|
||||
extern int32_t janet_gen_boot_size;
|
||||
|
||||
int main() {
|
||||
int status;
|
||||
JanetTable *env;
|
||||
|
||||
/* Set up VM */
|
||||
janet_init();
|
||||
env = janet_core_env();
|
||||
|
||||
/* Run bootstrap script to generate core image */
|
||||
status = janet_dobytes(env, janet_gen_boot, janet_gen_boot_size, "boot.janet", NULL);
|
||||
|
||||
/* Deinitialize vm */
|
||||
janet_deinit();
|
||||
|
||||
return status;
|
||||
}
|
||||
40
src/boot/boot.janet
Normal file
40
src/boot/boot.janet
Normal file
@@ -0,0 +1,40 @@
|
||||
# Copyright (C) Calvin Rose 2019
|
||||
|
||||
# The bootstrap script is used to produce the source file for
|
||||
# embedding the core image.
|
||||
|
||||
# Tool to dump a marshalled version of the janet core to stdout. The
|
||||
# image should eventually allow janet to be started from a pre-compiled
|
||||
# image rather than recompiled every time from the embedded source. More
|
||||
# work will go into shrinking the image (it isn't currently that large but
|
||||
# could be smaller), creating the mechanism to load the image, and modifying
|
||||
# the build process to compile janet with a built image rather than
|
||||
# embedded source.
|
||||
|
||||
# Get image. This image contains as much of the core library and documentation that
|
||||
# can be written to an image (no cfunctions, no abstracts (stdout, stdin, stderr)),
|
||||
# everything else goes. Cfunctions and abstracts will be referenced from a registry
|
||||
# table which will be generated on janet startup.
|
||||
(do
|
||||
(def image (let [env-pairs (pairs (env-lookup *env*))
|
||||
essential-pairs (filter (fn [[k v]] (or (cfunction? v) (abstract? v))) env-pairs)
|
||||
lookup (table ;(mapcat identity essential-pairs))
|
||||
reverse-lookup (invert lookup)]
|
||||
(marshal *env* reverse-lookup)))
|
||||
|
||||
# Create C source file that contains images a uint8_t buffer. This
|
||||
# can be compiled and linked statically into the main janet library
|
||||
# and example client.
|
||||
(def chunks (seq [b :in image] (string b)))
|
||||
(def image-file (file/open "build/core_image.c" :w))
|
||||
(file/write image-file
|
||||
"#include <janet/janet.h>\n"
|
||||
"static const unsigned char janet_core_image_bytes[] = {")
|
||||
(loop [line :in (partition 16 chunks)]
|
||||
(def str (string ;(interpose ", " line)))
|
||||
(file/write image-file str ",\n"))
|
||||
(file/write image-file
|
||||
"0};\n\n"
|
||||
"const unsigned char *janet_core_image = janet_core_image_bytes;\n"
|
||||
"size_t janet_core_image_size = sizeof(janet_core_image_bytes);\n")
|
||||
(file/close image-file))
|
||||
@@ -20,8 +20,10 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet/janet.h>
|
||||
#include "gc.h"
|
||||
#endif
|
||||
|
||||
/* Create new userdata */
|
||||
void *janet_abstract(const JanetAbstractType *atype, size_t size) {
|
||||
|
||||
@@ -20,9 +20,12 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet/janet.h>
|
||||
#include "gc.h"
|
||||
#include "util.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* Initializes an array */
|
||||
@@ -119,26 +122,26 @@ Janet janet_array_peek(JanetArray *array) {
|
||||
|
||||
/* C Functions */
|
||||
|
||||
static Janet cfun_new(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_array_new(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
int32_t cap = janet_getinteger(argv, 0);
|
||||
JanetArray *array = janet_array(cap);
|
||||
return janet_wrap_array(array);
|
||||
}
|
||||
|
||||
static Janet cfun_pop(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_array_pop(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetArray *array = janet_getarray(argv, 0);
|
||||
return janet_array_pop(array);
|
||||
}
|
||||
|
||||
static Janet cfun_peek(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_array_peek(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetArray *array = janet_getarray(argv, 0);
|
||||
return janet_array_peek(array);
|
||||
}
|
||||
|
||||
static Janet cfun_push(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_array_push(int32_t argc, Janet *argv) {
|
||||
janet_arity(argc, 1, -1);
|
||||
JanetArray *array = janet_getarray(argv, 0);
|
||||
int32_t newcount = array->count - 1 + argc;
|
||||
@@ -148,7 +151,7 @@ static Janet cfun_push(int32_t argc, Janet *argv) {
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static Janet cfun_ensure(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_array_ensure(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 3);
|
||||
JanetArray *array = janet_getarray(argv, 0);
|
||||
int32_t newcount = janet_getinteger(argv, 1);
|
||||
@@ -158,7 +161,7 @@ static Janet cfun_ensure(int32_t argc, Janet *argv) {
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static Janet cfun_slice(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_array_slice(int32_t argc, Janet *argv) {
|
||||
JanetRange range = janet_getslice(argc, argv);
|
||||
JanetView view = janet_getindexed(argv, 0);
|
||||
JanetArray *array = janet_array(range.end - range.start);
|
||||
@@ -167,7 +170,7 @@ static Janet cfun_slice(int32_t argc, Janet *argv) {
|
||||
return janet_wrap_array(array);
|
||||
}
|
||||
|
||||
static Janet cfun_concat(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_array_concat(int32_t argc, Janet *argv) {
|
||||
int32_t i;
|
||||
janet_arity(argc, 1, -1);
|
||||
JanetArray *array = janet_getarray(argv, 0);
|
||||
@@ -191,7 +194,7 @@ static Janet cfun_concat(int32_t argc, Janet *argv) {
|
||||
return janet_wrap_array(array);
|
||||
}
|
||||
|
||||
static Janet cfun_insert(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_array_insert(int32_t argc, Janet *argv) {
|
||||
size_t chunksize, restsize;
|
||||
janet_arity(argc, 2, -1);
|
||||
JanetArray *array = janet_getarray(argv, 0);
|
||||
@@ -212,47 +215,47 @@ static Janet cfun_insert(int32_t argc, Janet *argv) {
|
||||
return janet_wrap_array(array);
|
||||
}
|
||||
|
||||
static const JanetReg cfuns[] = {
|
||||
{"array/new", cfun_new,
|
||||
static const JanetReg array_cfuns[] = {
|
||||
{"array/new", cfun_array_new,
|
||||
JDOC("(array/new capacity)\n\n"
|
||||
"Creates a new empty array with a pre-allocated capacity. The same as "
|
||||
"(array) but can be more efficient if the maximum size of an array is known.")
|
||||
},
|
||||
{"array/pop", cfun_pop,
|
||||
{"array/pop", cfun_array_pop,
|
||||
JDOC("(array/pop arr)\n\n"
|
||||
"Remove the last element of the array and return it. If the array is empty, will return nil. Modifies "
|
||||
"the input array.")
|
||||
},
|
||||
{"array/peek", cfun_peek,
|
||||
{"array/peek", cfun_array_peek,
|
||||
JDOC("(array/peek arr)\n\n"
|
||||
"Returns the last element of the array. Does not modify the array.")
|
||||
},
|
||||
{"array/push", cfun_push,
|
||||
{"array/push", cfun_array_push,
|
||||
JDOC("(array/push arr x)\n\n"
|
||||
"Insert an element in the end of an array. Modifies the input array and returns it.")
|
||||
},
|
||||
{"array/ensure", cfun_ensure,
|
||||
{"array/ensure", cfun_array_ensure,
|
||||
JDOC("(array/ensure arr capacity)\n\n"
|
||||
"Ensures that the memory backing the array has enough memory for capacity "
|
||||
"items. Capacity must be an integer. If the backing capacity is already enough, "
|
||||
"then this function does nothing. Otherwise, the backing memory will be reallocated "
|
||||
"so that there is enough space.")
|
||||
},
|
||||
{"array/slice", cfun_slice,
|
||||
{"array/slice", cfun_array_slice,
|
||||
JDOC("(array/slice arrtup [, start=0 [, end=(length arrtup)]])\n\n"
|
||||
"Takes a slice of array or tuple from start to end. The range is half open, "
|
||||
"[start, end). Indexes can also be negative, indicating indexing from the end of the "
|
||||
"end of the array. By default, start is 0 and end is the length of the array. "
|
||||
"Returns a new array.")
|
||||
},
|
||||
{"array/concat", cfun_concat,
|
||||
{"array/concat", cfun_array_concat,
|
||||
JDOC("(array/concat arr & parts)\n\n"
|
||||
"Concatenates a variadic number of arrays (and tuples) into the first argument "
|
||||
"which must an array. If any of the parts are arrays or tuples, their elements will "
|
||||
"be inserted into the array. Otherwise, each part in parts will be appended to arr in order. "
|
||||
"Return the modified array arr.")
|
||||
},
|
||||
{"array/insert", cfun_insert,
|
||||
{"array/insert", cfun_array_insert,
|
||||
JDOC("(array/insert arr at & xs)\n\n"
|
||||
"Insert all of xs into array arr at index at. at should be an integer "
|
||||
"0 and the length of the array. A negative value for at will index from "
|
||||
@@ -264,5 +267,5 @@ static const JanetReg cfuns[] = {
|
||||
|
||||
/* Load the array module */
|
||||
void janet_lib_array(JanetTable *env) {
|
||||
janet_cfuns(env, NULL, cfuns);
|
||||
janet_core_cfuns(env, NULL, array_cfuns);
|
||||
}
|
||||
|
||||
@@ -20,9 +20,12 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <setjmp.h>
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet/janet.h>
|
||||
#include "util.h"
|
||||
#endif
|
||||
|
||||
#include <setjmp.h>
|
||||
|
||||
/* Conditionally compile this file */
|
||||
#ifdef JANET_ASSEMBLER
|
||||
@@ -79,9 +82,9 @@ static const JanetInstructionDef janet_ops[] = {
|
||||
{"get", JOP_GET},
|
||||
{"geti", JOP_GET_INDEX},
|
||||
{"gt", JOP_GREATER_THAN},
|
||||
{"gten", JOP_NUMERIC_GREATER_THAN_EQUAL},
|
||||
{"gtim", JOP_GREATER_THAN_IMMEDIATE},
|
||||
{"gtn", JOP_NUMERIC_GREATER_THAN},
|
||||
{"gten", JOP_NUMERIC_GREATER_THAN_EQUAL},
|
||||
{"jmp", JOP_JUMP},
|
||||
{"jmpif", JOP_JUMP_IF},
|
||||
{"jmpno", JOP_JUMP_IF_NOT},
|
||||
@@ -930,7 +933,7 @@ static Janet cfun_disasm(int32_t argc, Janet *argv) {
|
||||
return janet_disasm(f->def);
|
||||
}
|
||||
|
||||
static const JanetReg cfuns[] = {
|
||||
static const JanetReg asm_cfuns[] = {
|
||||
{"asm", cfun_asm,
|
||||
JDOC("(asm assembly)\n\n"
|
||||
"Returns a new function that is the compiled result of the assembly.\n"
|
||||
@@ -948,7 +951,7 @@ static const JanetReg cfuns[] = {
|
||||
|
||||
/* Load the library */
|
||||
void janet_lib_asm(JanetTable *env) {
|
||||
janet_cfuns(env, NULL, cfuns);
|
||||
janet_core_cfuns(env, NULL, asm_cfuns);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -20,9 +20,11 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet/janet.h>
|
||||
#include "gc.h"
|
||||
#include "util.h"
|
||||
#endif
|
||||
|
||||
/* Initialize a buffer */
|
||||
JanetBuffer *janet_buffer_init(JanetBuffer *buffer, int32_t capacity) {
|
||||
@@ -55,7 +57,8 @@ void janet_buffer_ensure(JanetBuffer *buffer, int32_t capacity, int32_t growth)
|
||||
uint8_t *new_data;
|
||||
uint8_t *old = buffer->data;
|
||||
if (capacity <= buffer->capacity) return;
|
||||
capacity *= growth;
|
||||
int64_t big_capacity = capacity * growth;
|
||||
capacity = big_capacity > INT32_MAX ? INT32_MAX : (int32_t) big_capacity;
|
||||
new_data = realloc(old, capacity * sizeof(uint8_t));
|
||||
if (NULL == new_data) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
@@ -154,14 +157,27 @@ void janet_buffer_push_u64(JanetBuffer *buffer, uint64_t x) {
|
||||
|
||||
/* C functions */
|
||||
|
||||
static Janet cfun_new(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_buffer_new(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
int32_t cap = janet_getinteger(argv, 0);
|
||||
JanetBuffer *buffer = janet_buffer(cap);
|
||||
return janet_wrap_buffer(buffer);
|
||||
}
|
||||
|
||||
static Janet cfun_u8(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_buffer_new_filled(int32_t argc, Janet *argv) {
|
||||
janet_arity(argc, 1, 2);
|
||||
int32_t count = janet_getinteger(argv, 0);
|
||||
int32_t byte = 0;
|
||||
if (argc == 2) {
|
||||
byte = janet_getinteger(argv, 1) & 0xFF;
|
||||
}
|
||||
JanetBuffer *buffer = janet_buffer(count);
|
||||
memset(buffer->data, byte, count);
|
||||
buffer->count = count;
|
||||
return janet_wrap_buffer(buffer);
|
||||
}
|
||||
|
||||
static Janet cfun_buffer_u8(int32_t argc, Janet *argv) {
|
||||
int32_t i;
|
||||
janet_arity(argc, 1, -1);
|
||||
JanetBuffer *buffer = janet_getbuffer(argv, 0);
|
||||
@@ -171,21 +187,21 @@ static Janet cfun_u8(int32_t argc, Janet *argv) {
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static Janet cfun_word(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_buffer_word(int32_t argc, Janet *argv) {
|
||||
int32_t i;
|
||||
janet_arity(argc, 1, -1);
|
||||
JanetBuffer *buffer = janet_getbuffer(argv, 0);
|
||||
for (i = 1; i < argc; i++) {
|
||||
double number = janet_getnumber(argv, 0);
|
||||
double number = janet_getnumber(argv, i);
|
||||
uint32_t word = (uint32_t) number;
|
||||
if (word != number)
|
||||
janet_panicf("cannot convert %v to machine word", argv[0]);
|
||||
janet_panicf("cannot convert %v to machine word", argv[i]);
|
||||
janet_buffer_push_u32(buffer, word);
|
||||
}
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static Janet cfun_chars(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_buffer_chars(int32_t argc, Janet *argv) {
|
||||
int32_t i;
|
||||
janet_arity(argc, 1, -1);
|
||||
JanetBuffer *buffer = janet_getbuffer(argv, 0);
|
||||
@@ -196,14 +212,14 @@ static Janet cfun_chars(int32_t argc, Janet *argv) {
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static Janet cfun_clear(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_buffer_clear(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetBuffer *buffer = janet_getbuffer(argv, 0);
|
||||
buffer->count = 0;
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static Janet cfun_popn(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_buffer_popn(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 2);
|
||||
JanetBuffer *buffer = janet_getbuffer(argv, 0);
|
||||
int32_t n = janet_getinteger(argv, 1);
|
||||
@@ -216,7 +232,7 @@ static Janet cfun_popn(int32_t argc, Janet *argv) {
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static Janet cfun_slice(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_buffer_slice(int32_t argc, Janet *argv) {
|
||||
JanetRange range = janet_getslice(argc, argv);
|
||||
JanetByteView view = janet_getbytes(argv, 0);
|
||||
JanetBuffer *buffer = janet_buffer(range.end - range.start);
|
||||
@@ -225,48 +241,151 @@ static Janet cfun_slice(int32_t argc, Janet *argv) {
|
||||
return janet_wrap_buffer(buffer);
|
||||
}
|
||||
|
||||
static const JanetReg cfuns[] = {
|
||||
{"buffer/new", cfun_new,
|
||||
static void bitloc(int32_t argc, Janet *argv, JanetBuffer **b, int32_t *index, int *bit) {
|
||||
janet_fixarity(argc, 2);
|
||||
JanetBuffer *buffer = janet_getbuffer(argv, 0);
|
||||
double x = janet_getnumber(argv, 1);
|
||||
int64_t bitindex = (int64_t) x;
|
||||
int64_t byteindex = bitindex >> 3;
|
||||
int which_bit = bitindex & 7;
|
||||
if (bitindex != x || bitindex < 0 || byteindex >= buffer->count)
|
||||
janet_panicf("invalid bit index %v", argv[1]);
|
||||
*b = buffer;
|
||||
*index = (int32_t) byteindex;
|
||||
*bit = which_bit;
|
||||
}
|
||||
|
||||
static Janet cfun_buffer_bitset(int32_t argc, Janet *argv) {
|
||||
int bit;
|
||||
int32_t index;
|
||||
JanetBuffer *buffer;
|
||||
bitloc(argc, argv, &buffer, &index, &bit);
|
||||
buffer->data[index] |= 1 << bit;
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static Janet cfun_buffer_bitclear(int32_t argc, Janet *argv) {
|
||||
int bit;
|
||||
int32_t index;
|
||||
JanetBuffer *buffer;
|
||||
bitloc(argc, argv, &buffer, &index, &bit);
|
||||
buffer->data[index] &= ~(1 << bit);
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static Janet cfun_buffer_bitget(int32_t argc, Janet *argv) {
|
||||
int bit;
|
||||
int32_t index;
|
||||
JanetBuffer *buffer;
|
||||
bitloc(argc, argv, &buffer, &index, &bit);
|
||||
return janet_wrap_boolean(buffer->data[index] & (1 << bit));
|
||||
}
|
||||
|
||||
static Janet cfun_buffer_bittoggle(int32_t argc, Janet *argv) {
|
||||
int bit;
|
||||
int32_t index;
|
||||
JanetBuffer *buffer;
|
||||
bitloc(argc, argv, &buffer, &index, &bit);
|
||||
buffer->data[index] ^= (1 << bit);
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static Janet cfun_buffer_blit(int32_t argc, Janet *argv) {
|
||||
janet_arity(argc, 2, 5);
|
||||
JanetBuffer *dest = janet_getbuffer(argv, 0);
|
||||
JanetByteView src = janet_getbytes(argv, 1);
|
||||
int32_t offset_dest = 0;
|
||||
int32_t offset_src = 0;
|
||||
if (argc > 2)
|
||||
offset_dest = janet_gethalfrange(argv, 2, dest->count, "dest-start");
|
||||
if (argc > 3)
|
||||
offset_src = janet_gethalfrange(argv, 3, src.len, "src-start");
|
||||
int32_t length_src;
|
||||
if (argc > 4) {
|
||||
int32_t src_end = janet_gethalfrange(argv, 4, src.len, "src-end");
|
||||
length_src = src_end - offset_src;
|
||||
if (length_src < 0) length_src = 0;
|
||||
} else {
|
||||
length_src = src.len - offset_src;
|
||||
}
|
||||
int64_t last = ((int64_t) offset_dest - offset_src) + length_src;
|
||||
if (last > INT32_MAX)
|
||||
janet_panic("buffer blit out of range");
|
||||
janet_buffer_ensure(dest, (int32_t) last, 2);
|
||||
if (last > dest->count) dest->count = (int32_t) last;
|
||||
memcpy(dest->data + offset_dest, src.bytes + offset_src, length_src);
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static const JanetReg buffer_cfuns[] = {
|
||||
{"buffer/new", cfun_buffer_new,
|
||||
JDOC("(buffer/new capacity)\n\n"
|
||||
"Creates a new, empty buffer with enough memory for capacity bytes. "
|
||||
"Returns a new buffer.")
|
||||
},
|
||||
{"buffer/push-byte", cfun_u8,
|
||||
{"buffer/new-filled", cfun_buffer_new_filled,
|
||||
JDOC("(buffer/new-filled count [, byte=0])\n\n"
|
||||
"Creates a new buffer of length count filled with byte. "
|
||||
"Returns the new buffer.")
|
||||
},
|
||||
{"buffer/push-byte", cfun_buffer_u8,
|
||||
JDOC("(buffer/push-byte buffer x)\n\n"
|
||||
"Append a byte to a buffer. Will expand the buffer as necessary. "
|
||||
"Returns the modified buffer. Will throw an error if the buffer overflows.")
|
||||
},
|
||||
{"buffer/push-word", cfun_word,
|
||||
{"buffer/push-word", cfun_buffer_word,
|
||||
JDOC("(buffer/push-word buffer x)\n\n"
|
||||
"Append a machine word to a buffer. The 4 bytes of the integer are appended "
|
||||
"in twos complement, big endian order, unsigned. Returns the modified buffer. Will "
|
||||
"throw an error if the buffer overflows.")
|
||||
},
|
||||
{"buffer/push-string", cfun_chars,
|
||||
{"buffer/push-string", cfun_buffer_chars,
|
||||
JDOC("(buffer/push-string buffer str)\n\n"
|
||||
"Push a string onto the end of a buffer. Non string values will be converted "
|
||||
"to strings before being pushed. Returns the modified buffer. "
|
||||
"Will throw an error if the buffer overflows.")
|
||||
},
|
||||
{"buffer/popn", cfun_popn,
|
||||
{"buffer/popn", cfun_buffer_popn,
|
||||
JDOC("(buffer/popn buffer n)\n\n"
|
||||
"Removes the last n bytes from the buffer. Returns the modified buffer.")
|
||||
},
|
||||
{"buffer/clear", cfun_clear,
|
||||
{"buffer/clear", cfun_buffer_clear,
|
||||
JDOC("(buffer/clear buffer)\n\n"
|
||||
"Sets the size of a buffer to 0 and empties it. The buffer retains "
|
||||
"its memory so it can be efficiently refilled. Returns the modified buffer.")
|
||||
},
|
||||
{"buffer/slice", cfun_slice,
|
||||
{"buffer/slice", cfun_buffer_slice,
|
||||
JDOC("(buffer/slice bytes [, start=0 [, end=(length bytes)]])\n\n"
|
||||
"Takes a slice of a byte sequence from start to end. The range is half open, "
|
||||
"[start, end). Indexes can also be negative, indicating indexing from the end of the "
|
||||
"end of the array. By default, start is 0 and end is the length of the buffer. "
|
||||
"Returns a new buffer.")
|
||||
},
|
||||
{"buffer/bit-set", cfun_buffer_bitset,
|
||||
JDOC("(buffer/bit-set buffer index)\n\n"
|
||||
"Sets the bit at the given bit-index. Returns the buffer.")
|
||||
},
|
||||
{"buffer/bit-clear", cfun_buffer_bitclear,
|
||||
JDOC("(buffer/bit-clear buffer index)\n\n"
|
||||
"Clears the bit at the given bit-index. Returns the buffer.")
|
||||
},
|
||||
{"buffer/bit", cfun_buffer_bitget,
|
||||
JDOC("(buffer/bit buffer index)\n\n"
|
||||
"Gets the bit at the given bit-index. Returns true if the bit is set, false if not.")
|
||||
},
|
||||
{"buffer/bit-toggle", cfun_buffer_bittoggle,
|
||||
JDOC("(buffer/bit-toggle buffer index)\n\n"
|
||||
"Toggles the bit at the given bit index in buffer. Returns the buffer.")
|
||||
},
|
||||
{"buffer/blit", cfun_buffer_blit,
|
||||
JDOC("(buffer/blit dest src [, dest-start=0 [, src-start=0 [, src-end=-1]]])\n\n"
|
||||
"Insert the contents of src into dest. Can optionally take indices that "
|
||||
"indicate which part of src to copy into which part of dest. Indices can be "
|
||||
"negative to index from the end of src or dest. Returns dest.")
|
||||
},
|
||||
{NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
void janet_lib_buffer(JanetTable *env) {
|
||||
janet_cfuns(env, NULL, cfuns);
|
||||
janet_core_cfuns(env, NULL, buffer_cfuns);
|
||||
}
|
||||
|
||||
@@ -20,8 +20,10 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet/janet.h>
|
||||
#include "gc.h"
|
||||
#endif
|
||||
|
||||
/* Look up table for instructions */
|
||||
enum JanetInstructionType janet_instructions[JOP_INSTRUCTION_COUNT] = {
|
||||
|
||||
@@ -20,14 +20,16 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet/janet.h>
|
||||
#include "state.h"
|
||||
#include "fiber.h"
|
||||
#endif
|
||||
|
||||
void janet_panicv(Janet message) {
|
||||
if (janet_vm_fiber != NULL) {
|
||||
janet_fiber_push(janet_vm_fiber, message);
|
||||
longjmp(janet_vm_fiber->buf, 1);
|
||||
if (janet_vm_return_reg != NULL) {
|
||||
*janet_vm_return_reg = message;
|
||||
longjmp(*janet_vm_jmp_buf, 1);
|
||||
} else {
|
||||
fputs((const char *)janet_formatc("janet top level panic - %v\n", message), stdout);
|
||||
exit(1);
|
||||
@@ -71,6 +73,16 @@ type janet_get##name(const Janet *argv, int32_t n) { \
|
||||
return janet_unwrap_##name(x); \
|
||||
}
|
||||
|
||||
Janet janet_getmethod(const uint8_t *method, const JanetMethod *methods) {
|
||||
while (methods->name) {
|
||||
if (!janet_cstrcmp(method, methods->name))
|
||||
return janet_wrap_cfunction(methods->cfun);
|
||||
methods++;
|
||||
}
|
||||
janet_panicf("unknown method %S invoked", method);
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
|
||||
DEFINE_GETTER(number, NUMBER, double)
|
||||
DEFINE_GETTER(array, ARRAY, JanetArray *)
|
||||
DEFINE_GETTER(tuple, TUPLE, const Janet *)
|
||||
@@ -110,6 +122,22 @@ int64_t janet_getinteger64(const Janet *argv, int32_t n) {
|
||||
return (int64_t) janet_unwrap_number(x);
|
||||
}
|
||||
|
||||
int32_t janet_gethalfrange(const Janet *argv, int32_t n, int32_t length, const char *which) {
|
||||
int32_t raw = janet_getinteger(argv, n);
|
||||
if (raw < 0) raw += length + 1;
|
||||
if (raw < 0 || raw > length)
|
||||
janet_panicf("%s index %d out of range [0,%d]", which, raw, length);
|
||||
return raw;
|
||||
}
|
||||
|
||||
int32_t janet_getargindex(const Janet *argv, int32_t n, int32_t length, const char *which) {
|
||||
int32_t raw = janet_getinteger(argv, n);
|
||||
if (raw < 0) raw += length;
|
||||
if (raw < 0 || raw > length)
|
||||
janet_panicf("%s index %d out of range [0,%d)", which, raw, length);
|
||||
return raw;
|
||||
}
|
||||
|
||||
JanetView janet_getindexed(const Janet *argv, int32_t n) {
|
||||
Janet x = argv[n];
|
||||
JanetView view;
|
||||
@@ -157,32 +185,13 @@ JanetRange janet_getslice(int32_t argc, const Janet *argv) {
|
||||
range.start = 0;
|
||||
range.end = length;
|
||||
} else if (argc == 2) {
|
||||
range.start = janet_getinteger(argv, 1);
|
||||
range.start = janet_gethalfrange(argv, 1, length, "start");
|
||||
range.end = length;
|
||||
if (range.start < 0) {
|
||||
range.start += length + 1;
|
||||
}
|
||||
if (range.start < 0 || range.start > length) {
|
||||
janet_panicf("slice start: index %d out of range [0,%d]", range.start, length);
|
||||
}
|
||||
} else {
|
||||
range.start = janet_getinteger(argv, 1);
|
||||
range.end = janet_getinteger(argv, 2);
|
||||
if (range.start < 0) {
|
||||
range.start += length + 1;
|
||||
}
|
||||
if (range.end < 0) {
|
||||
range.end += length + 1;
|
||||
}
|
||||
if (range.start < 0 || range.start > length) {
|
||||
janet_panicf("slice start: index %d out of range [0,%d]", range.start, length);
|
||||
}
|
||||
if (range.end < 0 || range.end > length) {
|
||||
janet_panicf("slice end: index %d out of range [0,%d]", range.end, length);
|
||||
}
|
||||
if (range.end < range.start) {
|
||||
range.start = janet_gethalfrange(argv, 1, length, "start");
|
||||
range.end = janet_gethalfrange(argv, 2, length, "end");
|
||||
if (range.end < range.start)
|
||||
range.end = range.start;
|
||||
}
|
||||
}
|
||||
return range;
|
||||
}
|
||||
|
||||
@@ -20,10 +20,12 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet/janet.h>
|
||||
#include "compile.h"
|
||||
#include "emit.h"
|
||||
#include "vector.h"
|
||||
#endif
|
||||
|
||||
static int fixarity0(JanetFopts opts, JanetSlot *args) {
|
||||
(void) opts;
|
||||
@@ -99,8 +101,15 @@ static JanetSlot do_get(JanetFopts opts, JanetSlot *args) {
|
||||
return opreduce(opts, args, JOP_GET, janet_wrap_nil());
|
||||
}
|
||||
static JanetSlot do_put(JanetFopts opts, JanetSlot *args) {
|
||||
janetc_emit_sss(opts.compiler, JOP_PUT, args[0], args[1], args[2], 0);
|
||||
return args[0];
|
||||
if (opts.flags & JANET_FOPTS_DROP) {
|
||||
janetc_emit_sss(opts.compiler, JOP_PUT, args[0], args[1], args[2], 0);
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
} else {
|
||||
JanetSlot t = janetc_gettarget(opts);
|
||||
janetc_copy(opts.compiler, t, args[0]);
|
||||
janetc_emit_sss(opts.compiler, JOP_PUT, t, args[1], args[2], 0);
|
||||
return t;
|
||||
}
|
||||
}
|
||||
static JanetSlot do_length(JanetFopts opts, JanetSlot *args) {
|
||||
return genericSS(opts, JOP_LENGTH, args[0]);
|
||||
|
||||
@@ -20,11 +20,13 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet/janet.h>
|
||||
#include "compile.h"
|
||||
#include "emit.h"
|
||||
#include "vector.h"
|
||||
#include "util.h"
|
||||
#endif
|
||||
|
||||
JanetFopts janetc_fopts_default(JanetCompiler *c) {
|
||||
JanetFopts ret;
|
||||
@@ -403,7 +405,9 @@ static JanetSlot janetc_call(JanetFopts opts, JanetSlot *slots, JanetSlot fun) {
|
||||
}
|
||||
if (!specialized) {
|
||||
janetc_pushslots(c, slots);
|
||||
if (opts.flags & JANET_FOPTS_TAIL) {
|
||||
if ((opts.flags & JANET_FOPTS_TAIL) &&
|
||||
/* Prevent top level tail calls for better errors */
|
||||
!(c->scope->flags & JANET_SCOPE_TOP)) {
|
||||
janetc_emit_s(c, JOP_TAILCALL, fun, 0);
|
||||
retslot = janetc_cslot(janet_wrap_nil());
|
||||
retslot.flags = JANET_SLOT_RETURNED;
|
||||
@@ -485,7 +489,7 @@ static int macroexpand1(
|
||||
JanetFiber *fiberp;
|
||||
JanetFunction *macro = janet_unwrap_function(macroval);
|
||||
int lock = janet_gclock();
|
||||
JanetSignal status = janet_call(
|
||||
JanetSignal status = janet_pcall(
|
||||
macro,
|
||||
janet_tuple_length(form) - 1,
|
||||
form + 1,
|
||||
@@ -553,7 +557,7 @@ JanetSlot janetc_value(JanetFopts opts, Janet x) {
|
||||
}
|
||||
break;
|
||||
case JANET_SYMBOL:
|
||||
ret = janetc_resolve(opts.compiler, janet_unwrap_symbol(x));
|
||||
ret = janetc_resolve(c, janet_unwrap_symbol(x));
|
||||
break;
|
||||
case JANET_ARRAY:
|
||||
ret = janetc_array(opts, x);
|
||||
@@ -576,13 +580,13 @@ JanetSlot janetc_value(JanetFopts opts, Janet x) {
|
||||
if (c->result.status == JANET_COMPILE_ERROR)
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
if (opts.flags & JANET_FOPTS_TAIL)
|
||||
ret = janetc_return(opts.compiler, ret);
|
||||
ret = janetc_return(c, ret);
|
||||
if (opts.flags & JANET_FOPTS_HINT) {
|
||||
janetc_copy(opts.compiler, opts.hint, ret);
|
||||
janetc_copy(c, opts.hint, ret);
|
||||
ret = opts.hint;
|
||||
}
|
||||
c->current_mapping = last_mapping;
|
||||
opts.compiler->recursion_guard++;
|
||||
c->recursion_guard++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -722,7 +726,7 @@ static Janet cfun(int32_t argc, Janet *argv) {
|
||||
}
|
||||
}
|
||||
|
||||
static const JanetReg cfuns[] = {
|
||||
static const JanetReg compile_cfuns[] = {
|
||||
{"compile", cfun,
|
||||
JDOC("(compile ast env [, source])\n\n"
|
||||
"Compiles an Abstract Syntax Tree (ast) into a janet function. "
|
||||
@@ -734,5 +738,5 @@ static const JanetReg cfuns[] = {
|
||||
};
|
||||
|
||||
void janet_lib_compile(JanetTable *env) {
|
||||
janet_cfuns(env, NULL, cfuns);
|
||||
janet_core_cfuns(env, NULL, compile_cfuns);
|
||||
}
|
||||
|
||||
@@ -23,8 +23,10 @@
|
||||
#ifndef JANET_COMPILE_H
|
||||
#define JANET_COMPILE_H
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet/janet.h>
|
||||
#include "regalloc.h"
|
||||
#endif
|
||||
|
||||
/* Tags for some functions for the prepared inliner */
|
||||
#define JANET_FUN_DEBUG 1
|
||||
|
||||
@@ -7,9 +7,7 @@
|
||||
###
|
||||
###
|
||||
|
||||
(var *env*
|
||||
"The current environment."
|
||||
_env)
|
||||
(var *env* "The current environment." _env)
|
||||
|
||||
(def defn :macro
|
||||
"(def name & more)\n\nDefine a function. Equivalent to (def name (fn name [args] ...))."
|
||||
@@ -92,7 +90,7 @@
|
||||
(defn function? "Check if x is a function (not a cfunction)." [x]
|
||||
(= (type x) :function))
|
||||
(defn cfunction? "Check if x a cfunction." [x] (= (type x) :cfunction))
|
||||
(defn table? "Check if x a table." [x] (= (type x) :table ))
|
||||
(defn table? "Check if x a table." [x] (= (type x) :table))
|
||||
(defn struct? "Check if x a struct." [x] (= (type x) :struct))
|
||||
(defn array? "Check if x is an array." [x] (= (type x) :array))
|
||||
(defn tuple? "Check if x is a tuple." [x] (= (type x) :tuple))
|
||||
@@ -725,7 +723,8 @@
|
||||
nil if not found. Note their is no way to differentiate a nil from the indexed collection
|
||||
and a not found. Consider find-index if this is an issue."
|
||||
[pred ind]
|
||||
(get ind (find-index pred ind)))
|
||||
(def i (find-index pred ind))
|
||||
(if (= i nil) nil (get ind i)))
|
||||
|
||||
(defn take-until
|
||||
"Given a predicate, take only elements from an indexed type that satisfy
|
||||
@@ -931,7 +930,7 @@ value, one key will be ignored."
|
||||
ret)
|
||||
|
||||
(defn zipcoll
|
||||
"Creates an table or tuple from two arrays/tuples.
|
||||
"Creates a table from two arrays/tuples.
|
||||
Returns a new table."
|
||||
[keys vals]
|
||||
(def res @{})
|
||||
@@ -1068,6 +1067,20 @@ value, one key will be ignored."
|
||||
(++ i))
|
||||
ret)
|
||||
|
||||
(defn partition
|
||||
"Partition an indexed data structure into tuples
|
||||
of size n. Returns a new array."
|
||||
[n ind]
|
||||
(var i 0) (var nextn n)
|
||||
(def len (length ind))
|
||||
(def ret (array/new (math/ceil (/ len n))))
|
||||
(while (<= nextn len)
|
||||
(array/push ret (tuple/slice ind i nextn))
|
||||
(set i nextn)
|
||||
(+= nextn n))
|
||||
(if (not= i len) (array/push ret (tuple/slice ind i)))
|
||||
ret)
|
||||
|
||||
###
|
||||
###
|
||||
### Pattern Matching
|
||||
@@ -1081,7 +1094,7 @@ value, one key will be ignored."
|
||||
[pattern expr onmatch seen]
|
||||
(cond
|
||||
|
||||
(and (symbol? pattern) (not (keyword? pattern)))
|
||||
(symbol? pattern)
|
||||
(if (get seen pattern)
|
||||
~(if (= ,pattern ,expr) ,(onmatch) ,sentinel)
|
||||
(do
|
||||
@@ -1140,14 +1153,13 @@ value, one key will be ignored."
|
||||
((fn aux [i]
|
||||
(cond
|
||||
(= i len-1) (get cases i)
|
||||
(< i len-1) (do
|
||||
(def $res (gensym))
|
||||
(< i len-1) (with-syms [$res]
|
||||
~(if (= ,sentinel (def ,$res ,(match-1 (get cases i) $x (fn [] (get cases (inc i))) @{})))
|
||||
,(aux (+ 2 i))
|
||||
,$res)))) 0)))
|
||||
|
||||
(put _env sentinel nil)
|
||||
(put _env match-1 nil)
|
||||
(put _env 'sentinel nil)
|
||||
(put _env 'match-1 nil)
|
||||
|
||||
###
|
||||
###
|
||||
@@ -1223,7 +1235,7 @@ value, one key will be ignored."
|
||||
(defmacro doc
|
||||
"Shows documentation for the given symbol."
|
||||
[sym]
|
||||
(tuple doc* '_env (tuple 'quote sym)))
|
||||
~(,doc* *env* ',sym))
|
||||
|
||||
###
|
||||
###
|
||||
@@ -1367,6 +1379,11 @@ value, one key will be ignored."
|
||||
(set current (macex1 current)))
|
||||
current)
|
||||
|
||||
(defn pp
|
||||
"Pretty print to stdout."
|
||||
[x]
|
||||
(print (string/pretty x)))
|
||||
|
||||
###
|
||||
###
|
||||
### Evaluation and Compilation
|
||||
@@ -1380,21 +1397,58 @@ value, one key will be ignored."
|
||||
[parent &]
|
||||
(def parent (if parent parent _env))
|
||||
(def newenv (table/setproto @{} parent))
|
||||
(put newenv '_env @{:value newenv :private true
|
||||
:doc "The environment table for the current scope."})
|
||||
newenv)
|
||||
|
||||
(defn bad-parse
|
||||
"Default handler for a parse error."
|
||||
[p where]
|
||||
(file/write stderr
|
||||
"parse error in "
|
||||
where
|
||||
" around byte "
|
||||
(string (parser/where p))
|
||||
": "
|
||||
(or (parser/error p) "unmatched delimiter")
|
||||
"\n"))
|
||||
|
||||
(defn bad-compile
|
||||
"Default handler for a compile error."
|
||||
[msg macrof where]
|
||||
(file/write stderr "compile error: " msg " while compiling " where "\n")
|
||||
(when macrof (debug/stacktrace macrof)))
|
||||
|
||||
(defn getline
|
||||
"Read a line from stdin into a buffer."
|
||||
[buf p &]
|
||||
(default buf @"")
|
||||
(when p (file/write stdout p))
|
||||
(file/read stdin :line buf)
|
||||
buf)
|
||||
|
||||
(defn run-context
|
||||
"Run a context. This evaluates expressions of janet in an environment,
|
||||
and is encapsulates the parsing, compilation, and evaluation of janet.
|
||||
env is the environment to evaluate the code in, chunks is a function
|
||||
that returns strings or buffers of source code (from a repl, file,
|
||||
network connection, etc. onstatus is a callback that is
|
||||
invoked when a result is returned or any other signal is raised.
|
||||
and is encapsulates the parsing, compilation, and evaluation.
|
||||
opts is a table or struct of options. The options are as follows:\n\n\t
|
||||
:chunks - callback to read into a buffer - default is getline\n\t
|
||||
:on-parse-error - callback when parsing fails - default is bad-parse\n\t
|
||||
:env - the environment to compile against - default is *env*\n\t
|
||||
:source - string path of source for better errors - default is \"<anonymous>\"\n\t
|
||||
:on-compile-error - callback when compilation fails - default is bad-compile\n\t
|
||||
:on-status - callback when a value is evaluated - default is debug/stacktrace"
|
||||
[opts]
|
||||
|
||||
This function can be used to implement a repl very easily, simply
|
||||
pass a function that reads line from stdin to chunks, status-pp to onstatus"
|
||||
[env chunks onstatus where &]
|
||||
(def {:env env
|
||||
:chunks chunks
|
||||
:on-status onstatus
|
||||
:on-compile-error on-compile-error
|
||||
:on-parse-error on-parse-error
|
||||
:source where} opts)
|
||||
(default env *env*)
|
||||
(default chunks getline)
|
||||
(default onstatus debug/stacktrace)
|
||||
(default on-compile-error bad-compile)
|
||||
(default on-parse-error bad-parse)
|
||||
(default where "<anonymous>")
|
||||
|
||||
# Are we done yet?
|
||||
(var going true)
|
||||
@@ -1414,17 +1468,14 @@ value, one key will be ignored."
|
||||
(do
|
||||
(set good false)
|
||||
(def {:error err :start start :end end :fiber errf} res)
|
||||
(onstatus
|
||||
:compile
|
||||
(def msg
|
||||
(if (<= 0 start)
|
||||
(string err "\n at (" start ":" end ")")
|
||||
err)
|
||||
errf
|
||||
where))))
|
||||
(string "compile error: " err " at (" start ":" end ")")
|
||||
err))
|
||||
(on-compile-error msg errf where))))
|
||||
:a))
|
||||
(def res (resume f nil))
|
||||
(when good
|
||||
(if going (onstatus (fiber/status f) res f where))))
|
||||
(when good (if going (onstatus f res))))
|
||||
|
||||
(def oldenv *env*)
|
||||
(set *env* env)
|
||||
@@ -1443,67 +1494,19 @@ value, one key will be ignored."
|
||||
(while (parser/has-more p)
|
||||
(eval1 (parser/produce p)))
|
||||
(when (= (parser/status p) :error)
|
||||
(onstatus :parse
|
||||
(string (parser/error p)
|
||||
" around byte " (parser/where p))
|
||||
nil
|
||||
where))))
|
||||
(on-parse-error p where))))
|
||||
|
||||
(if (= (parser/status p) :pending)
|
||||
(on-parse-error p where))
|
||||
|
||||
(set *env* oldenv)
|
||||
|
||||
env)
|
||||
|
||||
(defn status-pp
|
||||
"Pretty print a signal and associated state. Can be used as the
|
||||
onsignal argument to run-context."
|
||||
[sig x f source]
|
||||
(def title
|
||||
(case sig
|
||||
:parse "parse error"
|
||||
:compile "compile error"
|
||||
:error "error"
|
||||
(string "status " sig)))
|
||||
(file/write stderr
|
||||
(string title " in " source ": ")
|
||||
(if (bytes? x) x (string/pretty x))
|
||||
"\n")
|
||||
(when f
|
||||
(loop
|
||||
[nf :in (reverse (debug/lineage f))
|
||||
:before (file/write stderr " (fiber)\n")
|
||||
{:function func
|
||||
:tail tail
|
||||
:pc pc
|
||||
:c c
|
||||
:name name
|
||||
:source source
|
||||
:source-start start
|
||||
:source-end end} :in (debug/stack nf)]
|
||||
(file/write stderr " in")
|
||||
(when c (file/write stderr " cfunction"))
|
||||
(if name
|
||||
(file/write stderr " " name)
|
||||
(when func (file/write stderr " <anonymous>")))
|
||||
(if source
|
||||
(do
|
||||
(file/write stderr " [" source "]")
|
||||
(if start
|
||||
(file/write
|
||||
stderr
|
||||
" at ("
|
||||
(string start)
|
||||
":"
|
||||
(string end)
|
||||
")"))))
|
||||
(if (and (not start) pc)
|
||||
(file/write stderr " (pc=" (string pc) ")"))
|
||||
(when tail (file/write stderr " (tailcall)"))
|
||||
(file/write stderr "\n"))))
|
||||
|
||||
(defn eval-string
|
||||
"Evaluates a string in the current environment. If more control over the
|
||||
environment is needed, use run-context."
|
||||
[str]
|
||||
[str env &]
|
||||
(var state (string str))
|
||||
(defn chunks [buf _]
|
||||
(def ret state)
|
||||
@@ -1512,121 +1515,126 @@ value, one key will be ignored."
|
||||
(buffer/push-string buf str)
|
||||
(buffer/push-string buf "\n")))
|
||||
(var returnval nil)
|
||||
(run-context *env* chunks
|
||||
(fn [sig x f source]
|
||||
(if (= sig :dead)
|
||||
(set returnval x)
|
||||
(status-pp sig x f source)))
|
||||
"eval")
|
||||
(run-context {:env env
|
||||
:chunks chunks
|
||||
:on-compile-error error
|
||||
:on-parse-error error
|
||||
:on-status (fn [f val]
|
||||
(set returnval val)
|
||||
(if-not (= (fiber/status f) :dead)
|
||||
(debug/stacktrace f val)))
|
||||
:source "eval"})
|
||||
returnval)
|
||||
|
||||
(defn eval
|
||||
"Evaluates a form in the current environment. If more control over the
|
||||
environment is needed, use run-context."
|
||||
[form]
|
||||
(def res (compile form *env* "eval"))
|
||||
[form env &]
|
||||
(default env *env*)
|
||||
(def res (compile form env "eval"))
|
||||
(if (= (type res) :function)
|
||||
(res)
|
||||
(error (res :error))))
|
||||
|
||||
(do
|
||||
(def syspath (or (os/getenv "JANET_PATH") "/usr/local/lib/janet/"))
|
||||
(defglobal 'module/paths
|
||||
@["./?.janet"
|
||||
"./?/init.janet"
|
||||
"./janet_modules/?.janet"
|
||||
"./janet_modules/?/init.janet"
|
||||
(string syspath janet/version "/?.janet")
|
||||
(string syspath janet/version "/?/init.janet")
|
||||
(string syspath "/?.janet")
|
||||
(string syspath "/?/init.janet")])
|
||||
(defglobal 'module/native-paths
|
||||
@["./?.so"
|
||||
"./?/??.so"
|
||||
"./janet_modules/?.so"
|
||||
"./janet_modules/?/??.so"
|
||||
(string syspath janet/version "/?.so")
|
||||
(string syspath janet/version "/?/??.so")
|
||||
(string syspath "/?.so")
|
||||
(string syspath "/?/??.so")]))
|
||||
(def module/paths
|
||||
"The list of paths to look for modules. The following
|
||||
substitutions are preformed on each path. :sys: becomes
|
||||
module/*syspath*, :name: becomes the last part of the module
|
||||
name after the last /, and :all: is the module name literally.
|
||||
:native: becomes the dynamic library file extension, usually dll
|
||||
or so."
|
||||
@["./:all:.janet"
|
||||
"./:all:/init.janet"
|
||||
":sys:/:all:.janet"
|
||||
":sys:/:all:/init.janet"
|
||||
":all:"])
|
||||
|
||||
(if (= :windows (os/which))
|
||||
(loop [i :range [0 (length module/native-paths)]]
|
||||
(def x (get module/native-paths i))
|
||||
(put
|
||||
module/native-paths
|
||||
i
|
||||
(string/replace ".so" ".dll" x))))
|
||||
(def module/native-paths
|
||||
"See doc for module/paths"
|
||||
@["./:all:.:native:"
|
||||
"./:all:/:name:.:native:"
|
||||
":sys:/:all:.:native:"
|
||||
":sys:/:all:/:name:.:native:"
|
||||
":all:"])
|
||||
|
||||
(var module/*syspath*
|
||||
"The path where globally installed libraries are located.
|
||||
The default value is the environment variable JANET_PATH,
|
||||
and if that is not set /usr/local/lib/janet on linux/posix, and
|
||||
on Windows the default is the empty string."
|
||||
(or (os/getenv "JANET_PATH")
|
||||
(if (= :windows (os/which)) "" "/usr/local/lib/janet")))
|
||||
|
||||
(defn module/find
|
||||
"Try to match a module or path name from the patterns in paths."
|
||||
[path paths]
|
||||
(def parts (string/split "/" path))
|
||||
(def lastpart (get parts (- (length parts) 1)))
|
||||
(array/push
|
||||
(map (fn [x]
|
||||
(def y (string/replace "??" lastpart x))
|
||||
(string/replace "?" path y))
|
||||
paths)
|
||||
path))
|
||||
(def name (get parts (- (length parts) 1)))
|
||||
(def nati (if (= :windows (os/which)) "dll" "so"))
|
||||
(defn sub-path
|
||||
[p]
|
||||
(->> p
|
||||
(string/replace ":name:" name)
|
||||
(string/replace ":sys:" module/*syspath*)
|
||||
(string/replace ":native:" nati)
|
||||
(string/replace ":all:" path)))
|
||||
(map sub-path paths))
|
||||
|
||||
(def require
|
||||
"(require module & args)\n\n
|
||||
Require a module with the given name. Will search all of the paths in
|
||||
(def module/cache
|
||||
"Table mapping loaded module identifiers to their environments."
|
||||
@{})
|
||||
|
||||
(def module/loading
|
||||
"Table mapping currently loading modules to true. Used to prevent
|
||||
circular dependencies."
|
||||
@{})
|
||||
|
||||
# Require helpers
|
||||
(defn- fexists [path]
|
||||
(def f (file/open path))
|
||||
(if f (do (file/close f) path)))
|
||||
|
||||
(defn require
|
||||
"Require a module with the given name. Will search all of the paths in
|
||||
module/paths, then the path as a raw file path. Returns the new environment
|
||||
returned from compiling and running the file."
|
||||
(do
|
||||
[path & args]
|
||||
(def {:exit exit-on-error} (table ;args))
|
||||
(if-let [check (get module/cache path)]
|
||||
check
|
||||
(if-let [modpath (find fexists (module/find path module/paths))]
|
||||
(do
|
||||
(when (get module/loading modpath)
|
||||
(error (string "circular dependency: file " modpath " is loading")))
|
||||
# Normal janet module
|
||||
(def f (file/open modpath))
|
||||
(def newenv (make-env))
|
||||
(put module/loading modpath true)
|
||||
(defn chunks [buf _] (file/read f 2048 buf))
|
||||
(run-context {:env newenv
|
||||
:chunks chunks
|
||||
:on-status (fn [f x]
|
||||
(when (not= (fiber/status f) :dead)
|
||||
(debug/stacktrace f x)
|
||||
(if exit-on-error (os/exit 1))))
|
||||
:source modpath})
|
||||
(file/close f)
|
||||
(put module/loading modpath false)
|
||||
(put module/cache modpath newenv)
|
||||
(put module/cache path newenv)
|
||||
newenv)
|
||||
(do
|
||||
# Try native module
|
||||
(def n (find fexists (module/find path module/native-paths)))
|
||||
(if (not n)
|
||||
(error (string "could not open file for module " path)))
|
||||
(def e (make-env))
|
||||
(native n e)
|
||||
(put module/cache n e)
|
||||
(put module/cache path e)
|
||||
e))))
|
||||
|
||||
(defn check-mod
|
||||
[f testpath]
|
||||
(if f f (file/open testpath)))
|
||||
|
||||
(defn find-mod [path]
|
||||
(def paths (module/find path module/paths))
|
||||
(reduce check-mod nil paths))
|
||||
|
||||
(defn check-native
|
||||
[p testpath]
|
||||
(if p
|
||||
p
|
||||
(do
|
||||
(def f (file/open testpath))
|
||||
(if f (do (file/close f) testpath)))))
|
||||
|
||||
(defn find-native [path]
|
||||
(def paths (module/find path module/native-paths))
|
||||
(reduce check-native nil paths))
|
||||
|
||||
(def cache @{})
|
||||
(def loading @{})
|
||||
(fn require [path & args]
|
||||
(when (get loading path)
|
||||
(error (string "circular dependency: module " path " is loading")))
|
||||
(def {:exit exit-on-error} (table ;args))
|
||||
(if-let [check (get cache path)]
|
||||
check
|
||||
(if-let [f (find-mod path)]
|
||||
(do
|
||||
# Normal janet module
|
||||
(def newenv (make-env))
|
||||
(put cache path newenv)
|
||||
(put loading path true)
|
||||
(defn chunks [buf _] (file/read f 1024 buf))
|
||||
(run-context newenv chunks
|
||||
(fn [sig x f source]
|
||||
(when (not= sig :dead)
|
||||
(status-pp sig x f source)
|
||||
(if exit-on-error (os/exit 1))))
|
||||
path)
|
||||
(file/close f)
|
||||
(put loading path false)
|
||||
newenv)
|
||||
(do
|
||||
# Try native module
|
||||
(def n (find-native path))
|
||||
(if (not n)
|
||||
(error (string "could not open file for module " path)))
|
||||
(native n (make-env))))))))
|
||||
(put _env 'fexists nil)
|
||||
|
||||
(defn import*
|
||||
"Import a module into a given environment table. This is the
|
||||
@@ -1652,7 +1660,7 @@ value, one key will be ignored."
|
||||
x
|
||||
(string x)))
|
||||
args))
|
||||
(tuple import* '_env (string path) ;argm))
|
||||
(tuple import* '*env* (string path) ;argm))
|
||||
|
||||
(defn repl
|
||||
"Run a repl. The first parameter is an optional function to call to
|
||||
@@ -1661,17 +1669,25 @@ value, one key will be ignored."
|
||||
caught."
|
||||
[chunks onsignal &]
|
||||
(def newenv (make-env))
|
||||
(default chunks (fn [buf _] (file/read stdin :line buf)))
|
||||
(default onsignal (fn [sig x f source]
|
||||
(put newenv '_fiber @{:value f})
|
||||
(case sig
|
||||
(default onsignal (fn [f x]
|
||||
(case (fiber/status f)
|
||||
:dead (do
|
||||
(put newenv '_ @{:value x})
|
||||
(print (string/pretty x 20)))
|
||||
(status-pp sig x f source))))
|
||||
(run-context newenv chunks onsignal "repl"))
|
||||
(debug/stacktrace f x))))
|
||||
(run-context {:env newenv
|
||||
:chunks chunks
|
||||
:on-status onsignal
|
||||
:source "repl"}))
|
||||
|
||||
(defn all-symbols
|
||||
(defmacro meta
|
||||
"Add metadata to the current environment."
|
||||
[& args]
|
||||
(def opts (table ;args))
|
||||
(loop [[k v] :pairs opts]
|
||||
(put *env* k v)))
|
||||
|
||||
(defn all-bindings
|
||||
"Get all symbols available in the current environment."
|
||||
[env &]
|
||||
(default env *env*)
|
||||
@@ -1679,6 +1695,31 @@ value, one key will be ignored."
|
||||
(do (var e env) (while e (array/push envs e) (set e (table/getproto e))))
|
||||
(def symbol-set @{})
|
||||
(loop [envi :in envs
|
||||
k :keys envi]
|
||||
k :keys envi
|
||||
:when (symbol? k)]
|
||||
(put symbol-set k true))
|
||||
(sort (keys symbol-set)))
|
||||
|
||||
(defn slurp
|
||||
"Read all data from a file with name path
|
||||
and then close the file."
|
||||
[path]
|
||||
(def f (file/open path :r))
|
||||
(if-not f (error (string "could not open file " path)))
|
||||
(def contents (file/read f :all))
|
||||
(file/close f)
|
||||
contents)
|
||||
|
||||
(defn spit
|
||||
"Write contents to a file at path.
|
||||
Can optionally append to the file."
|
||||
[path contents mode &]
|
||||
(default mode :w)
|
||||
(def f (file/open path mode))
|
||||
(if-not f (error (string "could not open file " path " with mode " mode)))
|
||||
(file/write f contents)
|
||||
(file/close f)
|
||||
nil)
|
||||
|
||||
# Use dynamic *env* from now on
|
||||
(put _env '_env nil)
|
||||
|
||||
@@ -20,14 +20,21 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet/janet.h>
|
||||
#include "compile.h"
|
||||
#include "state.h"
|
||||
#include "util.h"
|
||||
#endif
|
||||
|
||||
/* Generated bytes */
|
||||
#ifdef JANET_BOOTSTRAP
|
||||
extern const unsigned char *janet_gen_core;
|
||||
extern int32_t janet_gen_core_size;
|
||||
#else
|
||||
extern const unsigned char *janet_core_image;
|
||||
extern size_t janet_core_image_size;
|
||||
#endif
|
||||
|
||||
/* Use LoadLibrary on windows or dlopen on posix to load dynamic libaries
|
||||
* with native code. */
|
||||
@@ -98,43 +105,31 @@ static Janet janet_core_print(int32_t argc, Janet *argv) {
|
||||
}
|
||||
|
||||
static Janet janet_core_describe(int32_t argc, Janet *argv) {
|
||||
JanetBuffer b;
|
||||
janet_buffer_init(&b, 0);
|
||||
JanetBuffer *b = janet_buffer(0);
|
||||
for (int32_t i = 0; i < argc; ++i)
|
||||
janet_description_b(&b, argv[i]);
|
||||
Janet ret = janet_stringv(b.data, b.count);
|
||||
janet_buffer_deinit(&b);
|
||||
return ret;
|
||||
janet_description_b(b, argv[i]);
|
||||
return janet_stringv(b->data, b->count);
|
||||
}
|
||||
|
||||
static Janet janet_core_string(int32_t argc, Janet *argv) {
|
||||
JanetBuffer b;
|
||||
janet_buffer_init(&b, 0);
|
||||
JanetBuffer *b = janet_buffer(0);
|
||||
for (int32_t i = 0; i < argc; ++i)
|
||||
janet_to_string_b(&b, argv[i]);
|
||||
Janet ret = janet_stringv(b.data, b.count);
|
||||
janet_buffer_deinit(&b);
|
||||
return ret;
|
||||
janet_to_string_b(b, argv[i]);
|
||||
return janet_stringv(b->data, b->count);
|
||||
}
|
||||
|
||||
static Janet janet_core_symbol(int32_t argc, Janet *argv) {
|
||||
JanetBuffer b;
|
||||
janet_buffer_init(&b, 0);
|
||||
JanetBuffer *b = janet_buffer(0);
|
||||
for (int32_t i = 0; i < argc; ++i)
|
||||
janet_to_string_b(&b, argv[i]);
|
||||
Janet ret = janet_symbolv(b.data, b.count);
|
||||
janet_buffer_deinit(&b);
|
||||
return ret;
|
||||
janet_to_string_b(b, argv[i]);
|
||||
return janet_symbolv(b->data, b->count);
|
||||
}
|
||||
|
||||
static Janet janet_core_keyword(int32_t argc, Janet *argv) {
|
||||
JanetBuffer b;
|
||||
janet_buffer_init(&b, 0);
|
||||
JanetBuffer *b = janet_buffer(0);
|
||||
for (int32_t i = 0; i < argc; ++i)
|
||||
janet_to_string_b(&b, argv[i]);
|
||||
Janet ret = janet_keywordv(b.data, b.count);
|
||||
janet_buffer_deinit(&b);
|
||||
return ret;
|
||||
janet_to_string_b(b, argv[i]);
|
||||
return janet_keywordv(b->data, b->count);
|
||||
}
|
||||
|
||||
static Janet janet_core_buffer(int32_t argc, Janet *argv) {
|
||||
@@ -152,7 +147,7 @@ static Janet janet_core_is_abstract(int32_t argc, Janet *argv) {
|
||||
static Janet janet_core_scannumber(int32_t argc, Janet *argv) {
|
||||
double number;
|
||||
janet_fixarity(argc, 1);
|
||||
JanetByteView view = janet_getbytes(argv, 1);
|
||||
JanetByteView view = janet_getbytes(argv, 0);
|
||||
if (janet_scan_number(view.bytes, view.len, &number))
|
||||
return janet_wrap_nil();
|
||||
return janet_wrap_number(number);
|
||||
@@ -248,7 +243,7 @@ static Janet janet_core_hash(int32_t argc, Janet *argv) {
|
||||
return janet_wrap_number(janet_hash(argv[0]));
|
||||
}
|
||||
|
||||
static const JanetReg cfuns[] = {
|
||||
static const JanetReg corelib_cfuns[] = {
|
||||
{"native", janet_core_native,
|
||||
JDOC("(native path [,env])\n\n"
|
||||
"Load a native module from the given path. The path "
|
||||
@@ -381,7 +376,7 @@ static const JanetReg cfuns[] = {
|
||||
{NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
#ifndef JANET_NO_BOOTSTRAP
|
||||
#ifdef JANET_BOOTSTRAP
|
||||
|
||||
/* Utility for inline assembly */
|
||||
static void janet_quick_asm(
|
||||
@@ -605,12 +600,9 @@ static const uint32_t bnot_asm[] = {
|
||||
|
||||
JanetTable *janet_core_env(void) {
|
||||
JanetTable *env = janet_table(0);
|
||||
Janet ret = janet_wrap_table(env);
|
||||
janet_core_cfuns(env, NULL, corelib_cfuns);
|
||||
|
||||
/* Load main functions */
|
||||
janet_cfuns(env, NULL, cfuns);
|
||||
|
||||
#ifndef JANET_NO_BOOTSTRAP
|
||||
#ifdef JANET_BOOTSTRAP
|
||||
janet_quick_asm(env, JANET_FUN_YIELD, "debug", 0, 1, debug_asm, sizeof(debug_asm),
|
||||
JDOC("(debug)\n\n"
|
||||
"Throws a debug signal that can be caught by a parent fiber and used to inspect "
|
||||
@@ -741,11 +733,11 @@ JanetTable *janet_core_env(void) {
|
||||
JDOC("The build identifier of the running janet program."));
|
||||
|
||||
/* Allow references to the environment */
|
||||
janet_def(env, "_env", ret, JDOC("The environment table for the current scope."));
|
||||
#endif
|
||||
janet_def(env, "_env", janet_wrap_table(env), JDOC("The environment table for the current scope."));
|
||||
|
||||
/* Set as gc root */
|
||||
janet_gcroot(janet_wrap_table(env));
|
||||
#endif
|
||||
|
||||
/* Load auxiliary envs */
|
||||
janet_lib_io(env);
|
||||
@@ -761,13 +753,31 @@ JanetTable *janet_core_env(void) {
|
||||
janet_lib_debug(env);
|
||||
janet_lib_string(env);
|
||||
janet_lib_marsh(env);
|
||||
janet_lib_peg(env);
|
||||
#ifdef JANET_ASSEMBLER
|
||||
janet_lib_asm(env);
|
||||
#endif
|
||||
|
||||
#ifndef JANET_NO_BOOTSTRAP
|
||||
#ifdef JANET_BOOTSTRAP
|
||||
/* Run bootstrap source */
|
||||
janet_dobytes(env, janet_gen_core, janet_gen_core_size, "core.janet", NULL);
|
||||
#else
|
||||
|
||||
/* Unmarshal from core image */
|
||||
Janet marsh_out;
|
||||
int status = janet_unmarshal(
|
||||
janet_core_image,
|
||||
janet_core_image_size,
|
||||
0,
|
||||
&marsh_out,
|
||||
env,
|
||||
NULL);
|
||||
if (status) {
|
||||
printf("error unmarshaling core image\n");
|
||||
exit(1);
|
||||
}
|
||||
janet_gcroot(marsh_out);
|
||||
env = janet_unwrap_table(marsh_out);
|
||||
#endif
|
||||
|
||||
return env;
|
||||
|
||||
117
src/core/debug.c
117
src/core/debug.c
@@ -20,10 +20,13 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet/janet.h>
|
||||
#include "gc.h"
|
||||
#include "state.h"
|
||||
#include "util.h"
|
||||
#include "vector.h"
|
||||
#endif
|
||||
|
||||
/* Implements functionality to build a debugger from within janet.
|
||||
* The repl should also be able to serve as pretty featured debugger
|
||||
@@ -88,6 +91,74 @@ void janet_debug_find(
|
||||
}
|
||||
}
|
||||
|
||||
/* Error reporting. This can be emulated from within Janet, but for
|
||||
* consitency with the top level code it is defined once. */
|
||||
void janet_stacktrace(JanetFiber *fiber, Janet err) {
|
||||
int32_t fi;
|
||||
const char *errstr = (const char *)janet_to_string(err);
|
||||
JanetFiber **fibers = NULL;
|
||||
int wrote_error = 0;
|
||||
|
||||
while (fiber) {
|
||||
janet_v_push(fibers, fiber);
|
||||
fiber = fiber->child;
|
||||
}
|
||||
|
||||
for (fi = janet_v_count(fibers) - 1; fi >= 0; fi--) {
|
||||
fiber = fibers[fi];
|
||||
int32_t i = fiber->frame;
|
||||
while (i > 0) {
|
||||
JanetStackFrame *frame = (JanetStackFrame *)(fiber->data + i - JANET_FRAME_SIZE);
|
||||
JanetFuncDef *def = NULL;
|
||||
i = frame->prevframe;
|
||||
|
||||
/* Print prelude to stack frame */
|
||||
if (!wrote_error) {
|
||||
JanetFiberStatus status = janet_fiber_status(fiber);
|
||||
const char *prefix = status == JANET_STATUS_ERROR ? "" : "status ";
|
||||
fprintf(stderr, "%s%s: %s\n",
|
||||
prefix,
|
||||
janet_status_names[status],
|
||||
errstr);
|
||||
wrote_error = 1;
|
||||
}
|
||||
|
||||
fprintf(stderr, " in");
|
||||
|
||||
if (frame->func) {
|
||||
def = frame->func->def;
|
||||
fprintf(stderr, " %s", def->name ? (const char *)def->name : "<anonymous>");
|
||||
if (def->source) {
|
||||
fprintf(stderr, " [%s]", (const char *)def->source);
|
||||
}
|
||||
} else {
|
||||
JanetCFunction cfun = (JanetCFunction)(frame->pc);
|
||||
if (cfun) {
|
||||
Janet name = janet_table_get(janet_vm_registry, janet_wrap_cfunction(cfun));
|
||||
if (!janet_checktype(name, JANET_NIL))
|
||||
fprintf(stderr, " %s", (const char *)janet_to_string(name));
|
||||
else
|
||||
fprintf(stderr, " <cfunction>");
|
||||
}
|
||||
}
|
||||
if (frame->flags & JANET_STACKFRAME_TAILCALL)
|
||||
fprintf(stderr, " (tailcall)");
|
||||
if (frame->func && frame->pc) {
|
||||
int32_t off = (int32_t) (frame->pc - def->bytecode);
|
||||
if (def->sourcemap) {
|
||||
JanetSourceMapping mapping = def->sourcemap[off];
|
||||
fprintf(stderr, " at (%d:%d)", mapping.start, mapping.end);
|
||||
} else {
|
||||
fprintf(stderr, " pc=%d", off);
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
janet_v_free(fibers);
|
||||
}
|
||||
|
||||
/*
|
||||
* CFuns
|
||||
*/
|
||||
@@ -111,7 +182,7 @@ static void helper_find_fun(int32_t argc, Janet *argv, JanetFuncDef **def, int32
|
||||
*bytecode_offset = offset;
|
||||
}
|
||||
|
||||
static Janet cfun_break(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_debug_break(int32_t argc, Janet *argv) {
|
||||
JanetFuncDef *def;
|
||||
int32_t offset;
|
||||
helper_find(argc, argv, &def, &offset);
|
||||
@@ -119,7 +190,7 @@ static Janet cfun_break(int32_t argc, Janet *argv) {
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
|
||||
static Janet cfun_unbreak(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_debug_unbreak(int32_t argc, Janet *argv) {
|
||||
JanetFuncDef *def;
|
||||
int32_t offset;
|
||||
helper_find(argc, argv, &def, &offset);
|
||||
@@ -127,7 +198,7 @@ static Janet cfun_unbreak(int32_t argc, Janet *argv) {
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
|
||||
static Janet cfun_fbreak(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_debug_fbreak(int32_t argc, Janet *argv) {
|
||||
JanetFuncDef *def;
|
||||
int32_t offset;
|
||||
helper_find_fun(argc, argv, &def, &offset);
|
||||
@@ -135,7 +206,7 @@ static Janet cfun_fbreak(int32_t argc, Janet *argv) {
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
|
||||
static Janet cfun_unfbreak(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_debug_unfbreak(int32_t argc, Janet *argv) {
|
||||
JanetFuncDef *def;
|
||||
int32_t offset;
|
||||
helper_find_fun(argc, argv, &def, &offset);
|
||||
@@ -143,7 +214,7 @@ static Janet cfun_unfbreak(int32_t argc, Janet *argv) {
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
|
||||
static Janet cfun_lineage(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_debug_lineage(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetFiber *fiber = janet_getfiber(argv, 0);
|
||||
JanetArray *array = janet_array(0);
|
||||
@@ -200,7 +271,7 @@ static Janet doframe(JanetStackFrame *frame) {
|
||||
return janet_wrap_table(t);
|
||||
}
|
||||
|
||||
static Janet cfun_stack(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_debug_stack(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetFiber *fiber = janet_getfiber(argv, 0);
|
||||
JanetArray *array = janet_array(0);
|
||||
@@ -216,7 +287,14 @@ static Janet cfun_stack(int32_t argc, Janet *argv) {
|
||||
return janet_wrap_array(array);
|
||||
}
|
||||
|
||||
static Janet cfun_argstack(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_debug_stacktrace(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 2);
|
||||
JanetFiber *fiber = janet_getfiber(argv, 0);
|
||||
janet_stacktrace(fiber, argv[1]);
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static Janet cfun_debug_argstack(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetFiber *fiber = janet_getfiber(argv, 0);
|
||||
JanetArray *array = janet_array(fiber->stacktop - fiber->stackstart);
|
||||
@@ -225,9 +303,9 @@ static Janet cfun_argstack(int32_t argc, Janet *argv) {
|
||||
return janet_wrap_array(array);
|
||||
}
|
||||
|
||||
static const JanetReg cfuns[] = {
|
||||
static const JanetReg debug_cfuns[] = {
|
||||
{
|
||||
"debug/break", cfun_break,
|
||||
"debug/break", cfun_debug_break,
|
||||
JDOC("(debug/break source byte-offset)\n\n"
|
||||
"Sets a breakpoint with source a key at a given byte offset. An offset "
|
||||
"of 0 is the first byte in a file. Will throw an error if the breakpoint location "
|
||||
@@ -236,33 +314,33 @@ static const JanetReg cfuns[] = {
|
||||
"wil set a breakpoint at the 1000th byte of the file core.janet.")
|
||||
},
|
||||
{
|
||||
"debug/unbreak", cfun_unbreak,
|
||||
"debug/unbreak", cfun_debug_unbreak,
|
||||
JDOC("(debug/unbreak source byte-offset)\n\n"
|
||||
"Remove a breakpoint with a source key at a given byte offset. An offset "
|
||||
"of 0 is the first byte in a file. Will throw an error if the breakpoint "
|
||||
"cannot be found.")
|
||||
},
|
||||
{
|
||||
"debug/fbreak", cfun_fbreak,
|
||||
"debug/fbreak", cfun_debug_fbreak,
|
||||
JDOC("(debug/fbreak fun [,pc=0])\n\n"
|
||||
"Set a breakpoint in a given function. pc is an optional offset, which "
|
||||
"is in bytecode instructions. fun is a function value. Will throw an error "
|
||||
"if the offset is too large or negative.")
|
||||
},
|
||||
{
|
||||
"debug/unfbreak", cfun_unfbreak,
|
||||
"debug/unfbreak", cfun_debug_unfbreak,
|
||||
JDOC("(debug/unfbreak fun [,pc=0])\n\n"
|
||||
"Unset a breakpoint set with debug/fbreak.")
|
||||
},
|
||||
{
|
||||
"debug/arg-stack", cfun_argstack,
|
||||
"debug/arg-stack", cfun_debug_argstack,
|
||||
JDOC("(debug/arg-stack fiber)\n\n"
|
||||
"Gets all values currently on the fiber's argument stack. Normally, "
|
||||
"this should be empty unless the fiber signals while pushing arguments "
|
||||
"to make a function call. Returns a new array.")
|
||||
},
|
||||
{
|
||||
"debug/stack", cfun_stack,
|
||||
"debug/stack", cfun_debug_stack,
|
||||
JDOC("(debug/stack fib)\n\n"
|
||||
"Gets information about the stack as an array of tables. Each table "
|
||||
"in the array contains information about a stack frame. The top most, current "
|
||||
@@ -279,7 +357,14 @@ static const JanetReg cfuns[] = {
|
||||
"\t:tail - boolean indicating a tail call")
|
||||
},
|
||||
{
|
||||
"debug/lineage", cfun_lineage,
|
||||
"debug/stacktrace", cfun_debug_stacktrace,
|
||||
JDOC("(debug/stacktrace fiber err)\n\n"
|
||||
"Prints a nice looking stacktrace for a fiber. The error message "
|
||||
"err must be passed to the function as fiber's do not keep track of "
|
||||
"the last error they have thrown. Returns the fiber.")
|
||||
},
|
||||
{
|
||||
"debug/lineage", cfun_debug_lineage,
|
||||
JDOC("(debug/lineage fib)\n\n"
|
||||
"Returns an array of all child fibers from a root fiber. This function "
|
||||
"is useful when a fiber signals or errors to an ancestor fiber. Using this function, "
|
||||
@@ -291,5 +376,5 @@ static const JanetReg cfuns[] = {
|
||||
|
||||
/* Module entry point */
|
||||
void janet_lib_debug(JanetTable *env) {
|
||||
janet_cfuns(env, NULL, cfuns);
|
||||
janet_core_cfuns(env, NULL, debug_cfuns);
|
||||
}
|
||||
|
||||
@@ -20,10 +20,12 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet/janet.h>
|
||||
#include "emit.h"
|
||||
#include "vector.h"
|
||||
#include "regalloc.h"
|
||||
#endif
|
||||
|
||||
/* Get a register */
|
||||
int32_t janetc_allocfar(JanetCompiler *c) {
|
||||
|
||||
@@ -23,7 +23,9 @@
|
||||
#ifndef JANET_EMIT_H
|
||||
#define JANET_EMIT_H
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include "compile.h"
|
||||
#endif
|
||||
|
||||
void janetc_emit(JanetCompiler *c, uint32_t instr);
|
||||
|
||||
|
||||
@@ -20,13 +20,25 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet/janet.h>
|
||||
#include "fiber.h"
|
||||
#include "state.h"
|
||||
#include "gc.h"
|
||||
#include "util.h"
|
||||
#endif
|
||||
|
||||
static JanetFiber *make_fiber(int32_t capacity) {
|
||||
static void fiber_reset(JanetFiber *fiber) {
|
||||
fiber->maxstack = JANET_STACK_MAX;
|
||||
fiber->frame = 0;
|
||||
fiber->stackstart = JANET_FRAME_SIZE;
|
||||
fiber->stacktop = JANET_FRAME_SIZE;
|
||||
fiber->child = NULL;
|
||||
fiber->flags = JANET_FIBER_MASK_YIELD;
|
||||
janet_fiber_set_status(fiber, JANET_STATUS_NEW);
|
||||
}
|
||||
|
||||
static JanetFiber *fiber_alloc(int32_t capacity) {
|
||||
Janet *data;
|
||||
JanetFiber *fiber = janet_gcalloc(JANET_MEMORY_FIBER, sizeof(JanetFiber));
|
||||
if (capacity < 32) {
|
||||
@@ -38,37 +50,31 @@ static JanetFiber *make_fiber(int32_t capacity) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
fiber->data = data;
|
||||
fiber->maxstack = JANET_STACK_MAX;
|
||||
fiber->frame = 0;
|
||||
fiber->stackstart = JANET_FRAME_SIZE;
|
||||
fiber->stacktop = JANET_FRAME_SIZE;
|
||||
fiber->child = NULL;
|
||||
fiber->flags = JANET_FIBER_MASK_YIELD;
|
||||
janet_fiber_set_status(fiber, JANET_STATUS_NEW);
|
||||
return fiber;
|
||||
}
|
||||
|
||||
/* Initialize a new fiber */
|
||||
JanetFiber *janet_fiber(JanetFunction *callee, int32_t capacity) {
|
||||
JanetFiber *fiber = make_fiber(capacity);
|
||||
if (janet_fiber_funcframe(fiber, callee)) return NULL;
|
||||
return fiber;
|
||||
}
|
||||
|
||||
/* Clear a fiber (reset it) with argn values on the stack. */
|
||||
JanetFiber *janet_fiber_n(JanetFunction *callee, int32_t capacity, const Janet *argv, int32_t argn) {
|
||||
/* Create a new fiber with argn values on the stack by reusing a fiber. */
|
||||
JanetFiber *janet_fiber_reset(JanetFiber *fiber, JanetFunction *callee, int32_t argc, const Janet *argv) {
|
||||
int32_t newstacktop;
|
||||
JanetFiber *fiber = make_fiber(capacity);
|
||||
newstacktop = fiber->stacktop + argn;
|
||||
if (newstacktop >= fiber->capacity) {
|
||||
janet_fiber_setcapacity(fiber, 2 * newstacktop);
|
||||
fiber_reset(fiber);
|
||||
if (argc) {
|
||||
newstacktop = fiber->stacktop + argc;
|
||||
if (newstacktop >= fiber->capacity) {
|
||||
janet_fiber_setcapacity(fiber, 2 * newstacktop);
|
||||
}
|
||||
memcpy(fiber->data + fiber->stacktop, argv, argc * sizeof(Janet));
|
||||
fiber->stacktop = newstacktop;
|
||||
}
|
||||
memcpy(fiber->data + fiber->stacktop, argv, argn * sizeof(Janet));
|
||||
fiber->stacktop = newstacktop;
|
||||
if (janet_fiber_funcframe(fiber, callee)) return NULL;
|
||||
janet_fiber_frame(fiber)->flags |= JANET_STACKFRAME_ENTRANCE;
|
||||
return fiber;
|
||||
}
|
||||
|
||||
/* Create a new fiber with argn values on the stack. */
|
||||
JanetFiber *janet_fiber(JanetFunction *callee, int32_t capacity, int32_t argc, const Janet *argv) {
|
||||
return janet_fiber_reset(fiber_alloc(capacity), callee, argc, argv);
|
||||
}
|
||||
|
||||
/* Ensure that the fiber has enough extra capacity */
|
||||
void janet_fiber_setcapacity(JanetFiber *fiber, int32_t n) {
|
||||
Janet *newData = realloc(fiber->data, sizeof(Janet) * n);
|
||||
@@ -293,7 +299,7 @@ void janet_fiber_popframe(JanetFiber *fiber) {
|
||||
|
||||
/* CFuns */
|
||||
|
||||
static Janet cfun_new(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_fiber_new(int32_t argc, Janet *argv) {
|
||||
janet_arity(argc, 1, 2);
|
||||
JanetFunction *func = janet_getfunction(argv, 0);
|
||||
JanetFiber *fiber;
|
||||
@@ -302,7 +308,7 @@ static Janet cfun_new(int32_t argc, Janet *argv) {
|
||||
janet_panic("expected nullary function in fiber constructor");
|
||||
}
|
||||
}
|
||||
fiber = janet_fiber(func, 64);
|
||||
fiber = janet_fiber(func, 64, 0, NULL);
|
||||
if (argc == 2) {
|
||||
int32_t i;
|
||||
JanetByteView view = janet_getbytes(argv, 1);
|
||||
@@ -342,7 +348,7 @@ static Janet cfun_new(int32_t argc, Janet *argv) {
|
||||
return janet_wrap_fiber(fiber);
|
||||
}
|
||||
|
||||
static Janet cfun_status(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_fiber_status(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetFiber *fiber = janet_getfiber(argv, 0);
|
||||
uint32_t s = (fiber->flags & JANET_FIBER_STATUS_MASK) >>
|
||||
@@ -350,19 +356,19 @@ static Janet cfun_status(int32_t argc, Janet *argv) {
|
||||
return janet_ckeywordv(janet_status_names[s]);
|
||||
}
|
||||
|
||||
static Janet cfun_current(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_fiber_current(int32_t argc, Janet *argv) {
|
||||
(void) argv;
|
||||
janet_fixarity(argc, 0);
|
||||
return janet_wrap_fiber(janet_vm_fiber);
|
||||
}
|
||||
|
||||
static Janet cfun_maxstack(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_fiber_maxstack(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetFiber *fiber = janet_getfiber(argv, 0);
|
||||
return janet_wrap_integer(fiber->maxstack);
|
||||
}
|
||||
|
||||
static Janet cfun_setmaxstack(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_fiber_setmaxstack(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 2);
|
||||
JanetFiber *fiber = janet_getfiber(argv, 0);
|
||||
int32_t maxs = janet_getinteger(argv, 1);
|
||||
@@ -373,9 +379,9 @@ static Janet cfun_setmaxstack(int32_t argc, Janet *argv) {
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static const JanetReg cfuns[] = {
|
||||
static const JanetReg fiber_cfuns[] = {
|
||||
{
|
||||
"fiber/new", cfun_new,
|
||||
"fiber/new", cfun_fiber_new,
|
||||
JDOC("(fiber/new func [,sigmask])\n\n"
|
||||
"Create a new fiber with function body func. Can optionally "
|
||||
"take a set of signals to block from the current parent fiber "
|
||||
@@ -393,7 +399,7 @@ static const JanetReg cfuns[] = {
|
||||
"\t0-9 - block a specific user signal")
|
||||
},
|
||||
{
|
||||
"fiber/status", cfun_status,
|
||||
"fiber/status", cfun_fiber_status,
|
||||
JDOC("(fiber/status fib)\n\n"
|
||||
"Get the status of a fiber. The status will be one of:\n\n"
|
||||
"\t:dead - the fiber has finished\n"
|
||||
@@ -405,19 +411,19 @@ static const JanetReg cfuns[] = {
|
||||
"\t:new - the fiber has just been created and not yet run")
|
||||
},
|
||||
{
|
||||
"fiber/current", cfun_current,
|
||||
"fiber/current", cfun_fiber_current,
|
||||
JDOC("(fiber/current)\n\n"
|
||||
"Returns the currently running fiber.")
|
||||
},
|
||||
{
|
||||
"fiber/maxstack", cfun_maxstack,
|
||||
"fiber/maxstack", cfun_fiber_maxstack,
|
||||
JDOC("(fiber/maxstack fib)\n\n"
|
||||
"Gets the maximum stack size in janet values allowed for a fiber. While memory for "
|
||||
"the fiber's stack is not allocated up front, the fiber will not allocated more "
|
||||
"than this amount and will throw a stack-overflow error if more memory is needed. ")
|
||||
},
|
||||
{
|
||||
"fiber/setmaxstack", cfun_setmaxstack,
|
||||
"fiber/setmaxstack", cfun_fiber_setmaxstack,
|
||||
JDOC("(fiber/setmaxstack fib maxstack)\n\n"
|
||||
"Sets the maximum stack size in janet values for a fiber. By default, the "
|
||||
"maximum stack size is usually 8192.")
|
||||
@@ -427,5 +433,5 @@ static const JanetReg cfuns[] = {
|
||||
|
||||
/* Module entry point */
|
||||
void janet_lib_fiber(JanetTable *env) {
|
||||
janet_cfuns(env, NULL, cfuns);
|
||||
janet_core_cfuns(env, NULL, fiber_cfuns);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,9 @@
|
||||
#ifndef JANET_FIBER_H_defined
|
||||
#define JANET_FIBER_H_defined
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet/janet.h>
|
||||
#endif
|
||||
|
||||
extern JANET_THREAD_LOCAL JanetFiber *janet_vm_fiber;
|
||||
|
||||
|
||||
@@ -20,10 +20,12 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet/janet.h>
|
||||
#include "state.h"
|
||||
#include "symcache.h"
|
||||
#include "gc.h"
|
||||
#endif
|
||||
|
||||
/* GC State */
|
||||
JANET_THREAD_LOCAL void *janet_vm_blocks;
|
||||
|
||||
@@ -23,7 +23,9 @@
|
||||
#ifndef JANET_GC_H
|
||||
#define JANET_GC_H
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet/janet.h>
|
||||
#endif
|
||||
|
||||
/* The metadata header associated with an allocated block of memory */
|
||||
#define janet_gc_header(mem) ((JanetGCMemoryHeader *)(mem) - 1)
|
||||
@@ -36,7 +38,6 @@
|
||||
#define janet_gc_type(m) (janet_gc_header(m)->flags & 0xFF)
|
||||
|
||||
#define janet_gc_mark(m) (janet_gc_header(m)->flags |= JANET_MEM_REACHABLE)
|
||||
#define janet_gc_unmark(m) (janet_gc_header(m)->flags &= ~JANET_MEM_COLOR)
|
||||
#define janet_gc_reachable(m) (janet_gc_header(m)->flags & JANET_MEM_REACHABLE)
|
||||
|
||||
/* Memory header struct. Node of a linked list of memory blocks. */
|
||||
|
||||
@@ -25,9 +25,12 @@
|
||||
#define _BSD_SOURCE
|
||||
|
||||
#include <stdio.h>
|
||||
#include <janet/janet.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet/janet.h>
|
||||
#include "util.h"
|
||||
#endif
|
||||
|
||||
#define IO_WRITE 1
|
||||
#define IO_READ 2
|
||||
@@ -45,11 +48,14 @@ struct IOFile {
|
||||
int flags;
|
||||
};
|
||||
|
||||
static int janet_io_gc(void *p, size_t len);
|
||||
static int cfun_io_gc(void *p, size_t len);
|
||||
static Janet io_file_get(void *p, Janet);
|
||||
|
||||
JanetAbstractType janet_io_filetype = {
|
||||
JanetAbstractType cfun_io_filetype = {
|
||||
"core/file",
|
||||
janet_io_gc,
|
||||
cfun_io_gc,
|
||||
NULL,
|
||||
io_file_get,
|
||||
NULL
|
||||
};
|
||||
|
||||
@@ -93,7 +99,7 @@ static int checkflags(const uint8_t *str) {
|
||||
}
|
||||
|
||||
static Janet makef(FILE *f, int flags) {
|
||||
IOFile *iof = (IOFile *) janet_abstract(&janet_io_filetype, sizeof(IOFile));
|
||||
IOFile *iof = (IOFile *) janet_abstract(&cfun_io_filetype, sizeof(IOFile));
|
||||
iof->file = f;
|
||||
iof->flags = flags;
|
||||
return janet_wrap_abstract(iof);
|
||||
@@ -101,27 +107,29 @@ static Janet makef(FILE *f, int flags) {
|
||||
|
||||
/* Open a process */
|
||||
#ifdef __EMSCRIPTEN__
|
||||
static Janet janet_io_popen(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_io_popen(int32_t argc, Janet *argv) {
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
janet_panic("not implemented on this platform");
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
#else
|
||||
static Janet janet_io_popen(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_io_popen(int32_t argc, Janet *argv) {
|
||||
janet_arity(argc, 1, 2);
|
||||
const uint8_t *fname = janet_getstring(argv, 0);
|
||||
const uint8_t *fmode = NULL;
|
||||
int flags;
|
||||
if (argc == 2) {
|
||||
fmode = janet_getkeyword(argv, 1);
|
||||
if (janet_string_length(fmode) != 1 ||
|
||||
!(fmode[0] == 'r' || fmode[0] == 'w')) {
|
||||
janet_panicf("invalid file mode :%S, expected :r or :w", fmode);
|
||||
}
|
||||
flags = IO_PIPED | (fmode[0] == 'r' ? IO_READ : IO_WRITE);
|
||||
} else {
|
||||
fmode = (const uint8_t *)"r";
|
||||
flags = IO_PIPED | IO_READ;
|
||||
}
|
||||
int flags = (fmode && fmode[0] == '2')
|
||||
? IO_PIPED | IO_WRITE
|
||||
: IO_PIPED | IO_READ;
|
||||
#ifdef JANET_WINDOWS
|
||||
#define popen _popen
|
||||
#endif
|
||||
@@ -133,7 +141,7 @@ static Janet janet_io_popen(int32_t argc, Janet *argv) {
|
||||
}
|
||||
#endif
|
||||
|
||||
static Janet janet_io_fopen(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_io_fopen(int32_t argc, Janet *argv) {
|
||||
janet_arity(argc, 1, 2);
|
||||
const uint8_t *fname = janet_getstring(argv, 0);
|
||||
const uint8_t *fmode;
|
||||
@@ -162,9 +170,9 @@ static void read_chunk(IOFile *iof, JanetBuffer *buffer, int32_t nBytesMax) {
|
||||
}
|
||||
|
||||
/* Read a certain number of bytes into memory */
|
||||
static Janet janet_io_fread(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_io_fread(int32_t argc, Janet *argv) {
|
||||
janet_arity(argc, 2, 3);
|
||||
IOFile *iof = janet_getabstract(argv, 0, &janet_io_filetype);
|
||||
IOFile *iof = janet_getabstract(argv, 0, &cfun_io_filetype);
|
||||
if (iof->flags & IO_CLOSED) janet_panic("file is closed");
|
||||
JanetBuffer *buffer;
|
||||
if (argc == 2) {
|
||||
@@ -208,9 +216,9 @@ static Janet janet_io_fread(int32_t argc, Janet *argv) {
|
||||
}
|
||||
|
||||
/* Write bytes to a file */
|
||||
static Janet janet_io_fwrite(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_io_fwrite(int32_t argc, Janet *argv) {
|
||||
janet_arity(argc, 1, -1);
|
||||
IOFile *iof = janet_getabstract(argv, 0, &janet_io_filetype);
|
||||
IOFile *iof = janet_getabstract(argv, 0, &cfun_io_filetype);
|
||||
if (iof->flags & IO_CLOSED)
|
||||
janet_panic("file is closed");
|
||||
if (!(iof->flags & (IO_WRITE | IO_APPEND | IO_UPDATE)))
|
||||
@@ -231,9 +239,9 @@ static Janet janet_io_fwrite(int32_t argc, Janet *argv) {
|
||||
}
|
||||
|
||||
/* Flush the bytes in the file */
|
||||
static Janet janet_io_fflush(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_io_fflush(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
IOFile *iof = janet_getabstract(argv, 0, &janet_io_filetype);
|
||||
IOFile *iof = janet_getabstract(argv, 0, &cfun_io_filetype);
|
||||
if (iof->flags & IO_CLOSED)
|
||||
janet_panic("file is closed");
|
||||
if (!(iof->flags & (IO_WRITE | IO_APPEND | IO_UPDATE)))
|
||||
@@ -244,7 +252,7 @@ static Janet janet_io_fflush(int32_t argc, Janet *argv) {
|
||||
}
|
||||
|
||||
/* Cleanup a file */
|
||||
static int janet_io_gc(void *p, size_t len) {
|
||||
static int cfun_io_gc(void *p, size_t len) {
|
||||
(void) len;
|
||||
IOFile *iof = (IOFile *)p;
|
||||
if (!(iof->flags & (IO_NOT_CLOSEABLE | IO_CLOSED))) {
|
||||
@@ -254,9 +262,9 @@ static int janet_io_gc(void *p, size_t len) {
|
||||
}
|
||||
|
||||
/* Close a file */
|
||||
static Janet janet_io_fclose(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_io_fclose(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
IOFile *iof = janet_getabstract(argv, 0, &janet_io_filetype);
|
||||
IOFile *iof = janet_getabstract(argv, 0, &cfun_io_filetype);
|
||||
if (iof->flags & IO_CLOSED)
|
||||
janet_panic("file is closed");
|
||||
if (iof->flags & (IO_NOT_CLOSEABLE))
|
||||
@@ -274,9 +282,9 @@ static Janet janet_io_fclose(int32_t argc, Janet *argv) {
|
||||
}
|
||||
|
||||
/* Seek a file */
|
||||
static Janet janet_io_fseek(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_io_fseek(int32_t argc, Janet *argv) {
|
||||
janet_arity(argc, 2, 3);
|
||||
IOFile *iof = janet_getabstract(argv, 0, &janet_io_filetype);
|
||||
IOFile *iof = janet_getabstract(argv, 0, &cfun_io_filetype);
|
||||
if (iof->flags & IO_CLOSED)
|
||||
janet_panic("file is closed");
|
||||
long int offset = 0;
|
||||
@@ -300,9 +308,25 @@ static Janet janet_io_fseek(int32_t argc, Janet *argv) {
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static const JanetReg cfuns[] = {
|
||||
static JanetMethod io_file_methods[] = {
|
||||
{"close", cfun_io_fclose},
|
||||
{"read", cfun_io_fread},
|
||||
{"write", cfun_io_fwrite},
|
||||
{"flush", cfun_io_fflush},
|
||||
{"seek", cfun_io_fseek},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
static Janet io_file_get(void *p, Janet key) {
|
||||
(void) p;
|
||||
if (!janet_checktype(key, JANET_KEYWORD))
|
||||
janet_panicf("expected keyword, got %v", key);
|
||||
return janet_getmethod(janet_unwrap_keyword(key), io_file_methods);
|
||||
}
|
||||
|
||||
static const JanetReg io_cfuns[] = {
|
||||
{
|
||||
"file/open", janet_io_fopen,
|
||||
"file/open", cfun_io_fopen,
|
||||
JDOC("(file/open path [,mode])\n\n"
|
||||
"Open a file. path is an absolute or relative path, and "
|
||||
"mode is a set of flags indicating the mode to open the file in. "
|
||||
@@ -316,14 +340,14 @@ static const JanetReg cfuns[] = {
|
||||
"\t+ - append to the file instead of overwriting it")
|
||||
},
|
||||
{
|
||||
"file/close", janet_io_fclose,
|
||||
"file/close", cfun_io_fclose,
|
||||
JDOC("(file/close f)\n\n"
|
||||
"Close a file and release all related resources. When you are "
|
||||
"done reading a file, close it to prevent a resource leak and let "
|
||||
"other processes read the file.")
|
||||
},
|
||||
{
|
||||
"file/read", janet_io_fread,
|
||||
"file/read", cfun_io_fread,
|
||||
JDOC("(file/read f what [,buf])\n\n"
|
||||
"Read a number of bytes from a file into a buffer. A buffer can "
|
||||
"be provided as an optional fourth argument, otherwise a new buffer "
|
||||
@@ -335,19 +359,19 @@ static const JanetReg cfuns[] = {
|
||||
"\tn (integer) - read up to n bytes from the file")
|
||||
},
|
||||
{
|
||||
"file/write", janet_io_fwrite,
|
||||
"file/write", cfun_io_fwrite,
|
||||
JDOC("(file/write f bytes)\n\n"
|
||||
"Writes to a file. 'bytes' must be string, buffer, or symbol. Returns the "
|
||||
"file.")
|
||||
},
|
||||
{
|
||||
"file/flush", janet_io_fflush,
|
||||
"file/flush", cfun_io_fflush,
|
||||
JDOC("(file/flush f)\n\n"
|
||||
"Flush any buffered bytes to the file system. In most files, writes are "
|
||||
"buffered for efficiency reasons. Returns the file handle.")
|
||||
},
|
||||
{
|
||||
"file/seek", janet_io_fseek,
|
||||
"file/seek", cfun_io_fseek,
|
||||
JDOC("(file/seek f [,whence [,n]])\n\n"
|
||||
"Jump to a relative location in the file. 'whence' must be one of\n\n"
|
||||
"\t:cur - jump relative to the current file location\n"
|
||||
@@ -358,7 +382,7 @@ static const JanetReg cfuns[] = {
|
||||
"number to handle large files of more the 4GB. Returns the file handle.")
|
||||
},
|
||||
{
|
||||
"file/popen", janet_io_popen,
|
||||
"file/popen", cfun_io_popen,
|
||||
JDOC("(file/popen path [,mode])\n\n"
|
||||
"Open a file that is backed by a process. The file must be opened in either "
|
||||
"the :r (read) or the :w (write) mode. In :r mode, the stdout of the "
|
||||
@@ -370,17 +394,18 @@ static const JanetReg cfuns[] = {
|
||||
|
||||
/* Module entry point */
|
||||
void janet_lib_io(JanetTable *env) {
|
||||
janet_cfuns(env, NULL, cfuns);
|
||||
janet_core_cfuns(env, NULL, io_cfuns);
|
||||
|
||||
/* stdout */
|
||||
janet_def(env, "stdout",
|
||||
janet_core_def(env, "stdout",
|
||||
makef(stdout, IO_APPEND | IO_NOT_CLOSEABLE | IO_SERIALIZABLE),
|
||||
JDOC("The standard output file."));
|
||||
/* stderr */
|
||||
janet_def(env, "stderr",
|
||||
janet_core_def(env, "stderr",
|
||||
makef(stderr, IO_APPEND | IO_NOT_CLOSEABLE | IO_SERIALIZABLE),
|
||||
JDOC("The standard error file."));
|
||||
/* stdin */
|
||||
janet_def(env, "stdin",
|
||||
janet_core_def(env, "stdin",
|
||||
makef(stdin, IO_READ | IO_NOT_CLOSEABLE | IO_SERIALIZABLE),
|
||||
JDOC("The standard input file."));
|
||||
}
|
||||
|
||||
@@ -20,13 +20,14 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet/janet.h>
|
||||
|
||||
#include "state.h"
|
||||
#include "vector.h"
|
||||
#include "gc.h"
|
||||
#include "fiber.h"
|
||||
#include "util.h"
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
jmp_buf err;
|
||||
@@ -123,15 +124,20 @@ JanetTable *janet_env_lookup(JanetTable *env) {
|
||||
|
||||
/* Marshal an integer onto the buffer */
|
||||
static void pushint(MarshalState *st, int32_t x) {
|
||||
if (x >= 0 && x < 200) {
|
||||
if (x >= 0 && x < 128) {
|
||||
janet_buffer_push_u8(st->buf, x);
|
||||
} else if (x <= 8191 && x >= -8192) {
|
||||
uint8_t intbuf[2];
|
||||
intbuf[0] = ((x >> 8) & 0x3F) | 0x80;
|
||||
intbuf[1] = x & 0xFF;
|
||||
janet_buffer_push_bytes(st->buf, intbuf, 2);
|
||||
} else {
|
||||
uint8_t intbuf[5];
|
||||
intbuf[0] = LB_INTEGER;
|
||||
intbuf[1] = x & 0xFF;
|
||||
intbuf[2] = (x >> 8) & 0xFF;
|
||||
intbuf[3] = (x >> 16) & 0xFF;
|
||||
intbuf[4] = (x >> 24) & 0xFF;
|
||||
intbuf[1] = (x >> 24) & 0xFF;
|
||||
intbuf[2] = (x >> 16) & 0xFF;
|
||||
intbuf[3] = (x >> 8) & 0xFF;
|
||||
intbuf[4] = x & 0xFF;
|
||||
janet_buffer_push_bytes(st->buf, intbuf, 5);
|
||||
}
|
||||
}
|
||||
@@ -233,10 +239,12 @@ static void marshal_one_def(MarshalState *st, JanetFuncDef *def, int flags) {
|
||||
|
||||
/* marshal source maps if needed */
|
||||
if (def->flags & JANET_FUNCDEF_FLAG_HASSOURCEMAP) {
|
||||
int32_t current = 0;
|
||||
for (int32_t i = 0; i < def->bytecode_length; i++) {
|
||||
JanetSourceMapping map = def->sourcemap[i];
|
||||
pushint(st, map.start);
|
||||
pushint(st, map.end);
|
||||
pushint(st, map.start - current);
|
||||
pushint(st, map.end - map.start);
|
||||
current = map.end;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -545,14 +553,19 @@ static int32_t readint(UnmarshalState *st, const uint8_t **atdata) {
|
||||
const uint8_t *data = *atdata;
|
||||
int32_t ret;
|
||||
if (data >= st->end) longjmp(st->err, UMR_EOS);
|
||||
if (*data < 200) {
|
||||
if (*data < 128) {
|
||||
ret = *data++;
|
||||
} else if (*data < 192) {
|
||||
if (data + 2 > st->end) longjmp(st->err, UMR_EOS);
|
||||
ret = ((data[0] & 0x3F) << 8) + data[1];
|
||||
ret = ((ret << 18) >> 18);
|
||||
data += 2;
|
||||
} else if (*data == LB_INTEGER) {
|
||||
if (data + 5 > st->end) longjmp(st->err, UMR_EOS);
|
||||
ret = (data[1]) |
|
||||
(data[2] << 8) |
|
||||
(data[3] << 16) |
|
||||
(data[4] << 24);
|
||||
ret = (data[1] << 24) |
|
||||
(data[2] << 16) |
|
||||
(data[3] << 8) |
|
||||
data[4];
|
||||
data += 5;
|
||||
} else {
|
||||
longjmp(st->err, UMR_EXPECTED_INTEGER);
|
||||
@@ -747,13 +760,16 @@ static const uint8_t *unmarshal_one_def(
|
||||
|
||||
/* Unmarshal source maps if needed */
|
||||
if (def->flags & JANET_FUNCDEF_FLAG_HASSOURCEMAP) {
|
||||
int32_t current = 0;
|
||||
def->sourcemap = malloc(sizeof(JanetSourceMapping) * bytecode_length);
|
||||
if (!def->sourcemap) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
for (int32_t i = 0; i < bytecode_length; i++) {
|
||||
def->sourcemap[i].start = readint(st, &data);
|
||||
def->sourcemap[i].end = readint(st, &data);
|
||||
current += readint(st, &data);
|
||||
def->sourcemap[i].start = current;
|
||||
current += readint(st, &data);
|
||||
def->sourcemap[i].end = current;
|
||||
}
|
||||
} else {
|
||||
def->sourcemap = NULL;
|
||||
@@ -904,8 +920,8 @@ static const uint8_t *unmarshal_one(
|
||||
EXTRA(1);
|
||||
lead = data[0];
|
||||
if (lead < 200) {
|
||||
*out = janet_wrap_integer(lead);
|
||||
return data + 1;
|
||||
*out = janet_wrap_integer(readint(st, &data));
|
||||
return data;
|
||||
}
|
||||
switch (lead) {
|
||||
case LB_NIL:
|
||||
@@ -1144,7 +1160,7 @@ static Janet cfun_unmarshal(int32_t argc, Janet *argv) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const JanetReg cfuns[] = {
|
||||
static const JanetReg marsh_cfuns[] = {
|
||||
{
|
||||
"marshal", cfun_marshal,
|
||||
JDOC("(marshal x [,reverse-lookup [,buffer]])\n\n"
|
||||
@@ -1174,5 +1190,5 @@ static const JanetReg cfuns[] = {
|
||||
|
||||
/* Module entry point */
|
||||
void janet_lib_marsh(JanetTable *env) {
|
||||
janet_cfuns(env, NULL, cfuns);
|
||||
janet_core_cfuns(env, NULL, marsh_cfuns);
|
||||
}
|
||||
|
||||
@@ -20,9 +20,12 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <janet/janet.h>
|
||||
#include <math.h>
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet/janet.h>
|
||||
#include "util.h"
|
||||
#endif
|
||||
|
||||
/* Get a random number */
|
||||
Janet janet_rand(int32_t argc, Janet *argv) {
|
||||
@@ -87,7 +90,7 @@ static Janet janet_not(int32_t argc, Janet *argv) {
|
||||
return janet_wrap_boolean(!janet_truthy(argv[0]));
|
||||
}
|
||||
|
||||
static const JanetReg cfuns[] = {
|
||||
static const JanetReg math_cfuns[] = {
|
||||
{
|
||||
"%", janet_remainder,
|
||||
JDOC("(% dividend divisor)\n\n"
|
||||
@@ -178,8 +181,8 @@ static const JanetReg cfuns[] = {
|
||||
|
||||
/* Module entry point */
|
||||
void janet_lib_math(JanetTable *env) {
|
||||
janet_cfuns(env, NULL, cfuns);
|
||||
#ifndef JANET_NO_BOOTSTRAP
|
||||
janet_core_cfuns(env, NULL, math_cfuns);
|
||||
#ifdef JANET_BOOTSTRAP
|
||||
janet_def(env, "math/pi", janet_wrap_number(3.1415926535897931),
|
||||
JDOC("The value pi."));
|
||||
janet_def(env, "math/e", janet_wrap_number(2.7182818284590451),
|
||||
|
||||
@@ -20,10 +20,13 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet/janet.h>
|
||||
#include "util.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include "util.h"
|
||||
|
||||
#ifdef JANET_WINDOWS
|
||||
#include <Windows.h>
|
||||
@@ -272,7 +275,31 @@ static Janet os_cwd(int32_t argc, Janet *argv) {
|
||||
return janet_cstringv(ptr);
|
||||
}
|
||||
|
||||
static const JanetReg cfuns[] = {
|
||||
static Janet os_date(int32_t argc, Janet *argv) {
|
||||
janet_arity(argc, 0, 1);
|
||||
(void) argv;
|
||||
time_t t;
|
||||
struct tm *t_info;
|
||||
if (argc) {
|
||||
t = (time_t) janet_getinteger64(argv, 0);
|
||||
} else {
|
||||
time(&t);
|
||||
}
|
||||
t_info = localtime(&t);
|
||||
JanetKV *st = janet_struct_begin(9);
|
||||
janet_struct_put(st, janet_ckeywordv("seconds"), janet_wrap_number(t_info->tm_sec));
|
||||
janet_struct_put(st, janet_ckeywordv("minutes"), janet_wrap_number(t_info->tm_min));
|
||||
janet_struct_put(st, janet_ckeywordv("hours"), janet_wrap_number(t_info->tm_hour));
|
||||
janet_struct_put(st, janet_ckeywordv("month-day"), janet_wrap_number(t_info->tm_mday - 1));
|
||||
janet_struct_put(st, janet_ckeywordv("month"), janet_wrap_number(t_info->tm_mon));
|
||||
janet_struct_put(st, janet_ckeywordv("year"), janet_wrap_number(t_info->tm_year + 1900));
|
||||
janet_struct_put(st, janet_ckeywordv("week-day"), janet_wrap_number(t_info->tm_wday));
|
||||
janet_struct_put(st, janet_ckeywordv("year-day"), janet_wrap_number(t_info->tm_yday));
|
||||
janet_struct_put(st, janet_ckeywordv("dst"), janet_wrap_boolean(t_info->tm_isdst));
|
||||
return janet_wrap_struct(janet_struct_end(st));
|
||||
}
|
||||
|
||||
static const JanetReg os_cfuns[] = {
|
||||
{
|
||||
"os/which", os_which,
|
||||
JDOC("(os/which)\n\n"
|
||||
@@ -318,7 +345,7 @@ static const JanetReg cfuns[] = {
|
||||
"os/clock", os_clock,
|
||||
JDOC("(os/clock)\n\n"
|
||||
"Return the number of seconds since some fixed point in time. The clock "
|
||||
"is guaranteed to be non decreased in real time.")
|
||||
"is guaranteed to be non decreasing in real time.")
|
||||
},
|
||||
{
|
||||
"os/sleep", os_sleep,
|
||||
@@ -331,10 +358,25 @@ static const JanetReg cfuns[] = {
|
||||
JDOC("(os/cwd)\n\n"
|
||||
"Returns the current working directory.")
|
||||
},
|
||||
{
|
||||
"os/date", os_date,
|
||||
JDOC("(os/date [,time])\n\n"
|
||||
"Returns the given time as a date struct, or the current time if no time is given. "
|
||||
"Returns a struct with following key values. Note that all numbers are 0-indexed.\n\n"
|
||||
"\t:seconds - number of seconds [0-61]\n"
|
||||
"\t:minutes - number of minutes [0-59]\n"
|
||||
"\t:seconds - number of hours [0-23]\n"
|
||||
"\t:month-day - day of month [0-30]\n"
|
||||
"\t:month - month of year [0, 11]\n"
|
||||
"\t:year - years since year 0 (e.g. 2019)\n"
|
||||
"\t:week-day - day of the week [0-6]\n"
|
||||
"\t:year-day - day of the year [0-365]\n"
|
||||
"\t:dst - If Day Light Savings is in effect")
|
||||
},
|
||||
{NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
/* Module entry point */
|
||||
void janet_lib_os(JanetTable *env) {
|
||||
janet_cfuns(env, NULL, cfuns);
|
||||
janet_core_cfuns(env, NULL, os_cfuns);
|
||||
}
|
||||
|
||||
120
src/core/parse.c
120
src/core/parse.c
@@ -20,8 +20,10 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet/janet.h>
|
||||
#include "util.h"
|
||||
#endif
|
||||
|
||||
/* Check if a character is whitespace */
|
||||
static int is_whitespace(uint8_t c) {
|
||||
@@ -293,9 +295,11 @@ static int tokenchar(JanetParser *p, JanetParseState *state, uint8_t c) {
|
||||
}
|
||||
/* Token finished */
|
||||
blen = (int32_t) p->bufcount;
|
||||
int start_dig = p->buf[0] >= '0' && p->buf[0] <= '9';
|
||||
int start_num = start_dig || p->buf[0] == '-' || p->buf[0] == '+' || p->buf[0] == '.';
|
||||
if (p->buf[0] == ':') {
|
||||
ret = janet_keywordv(p->buf + 1, blen - 1);
|
||||
} else if (!janet_scan_number(p->buf, blen, &numval)) {
|
||||
} else if (start_num && !janet_scan_number(p->buf, blen, &numval)) {
|
||||
ret = janet_wrap_number(numval);
|
||||
} else if (!check_str_const("nil", p->buf, blen)) {
|
||||
ret = janet_wrap_nil();
|
||||
@@ -304,7 +308,7 @@ static int tokenchar(JanetParser *p, JanetParseState *state, uint8_t c) {
|
||||
} else if (!check_str_const("true", p->buf, blen)) {
|
||||
ret = janet_wrap_true();
|
||||
} else if (p->buf) {
|
||||
if (p->buf[0] >= '0' && p->buf[0] <= '9') {
|
||||
if (start_dig) {
|
||||
p->error = "symbol literal cannot start with a digit";
|
||||
return 0;
|
||||
} else {
|
||||
@@ -474,7 +478,7 @@ static int root(JanetParser *p, JanetParseState *state, uint8_t c) {
|
||||
{
|
||||
Janet ds;
|
||||
if (p->statecount == 1) {
|
||||
p->error = "mismatched delimiter";
|
||||
p->error = "unexpected delimiter";
|
||||
return 1;
|
||||
}
|
||||
if ((c == ')' && (state->flags & PFLAG_PARENS)) ||
|
||||
@@ -605,14 +609,18 @@ static int parsergc(void *p, size_t size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Janet parserget(void *p, Janet key);
|
||||
|
||||
static JanetAbstractType janet_parse_parsertype = {
|
||||
"core/parser",
|
||||
parsergc,
|
||||
parsermark
|
||||
parsermark,
|
||||
parserget,
|
||||
NULL
|
||||
};
|
||||
|
||||
/* C Function parser */
|
||||
static Janet cfun_parser(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_parse_parser(int32_t argc, Janet *argv) {
|
||||
(void) argv;
|
||||
janet_fixarity(argc, 0);
|
||||
JanetParser *p = janet_abstract(&janet_parse_parsertype, sizeof(JanetParser));
|
||||
@@ -620,7 +628,7 @@ static Janet cfun_parser(int32_t argc, Janet *argv) {
|
||||
return janet_wrap_abstract(p);
|
||||
}
|
||||
|
||||
static Janet cfun_consume(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_parse_consume(int32_t argc, Janet *argv) {
|
||||
janet_arity(argc, 2, 3);
|
||||
JanetParser *p = janet_getabstract(argv, 0, &janet_parse_parsertype);
|
||||
JanetByteView view = janet_getbytes(argv, 1);
|
||||
@@ -645,13 +653,46 @@ static Janet cfun_consume(int32_t argc, Janet *argv) {
|
||||
return janet_wrap_integer(i);
|
||||
}
|
||||
|
||||
static Janet cfun_has_more(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_parse_insert(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 2);
|
||||
JanetParser *p = janet_getabstract(argv, 0, &janet_parse_parsertype);
|
||||
JanetParseState *s = p->states + p->statecount - 1;
|
||||
if (s->consumer == tokenchar) {
|
||||
janet_parser_consume(p, ' ');
|
||||
p->offset--;
|
||||
s = p->states + p->statecount - 1;
|
||||
}
|
||||
if (s->flags & PFLAG_CONTAINER) {
|
||||
s->argn++;
|
||||
if (p->statecount == 1) p->pending++;
|
||||
push_arg(p, argv[1]);
|
||||
} else if (s->flags & (PFLAG_STRING | PFLAG_LONGSTRING)) {
|
||||
const uint8_t *str = janet_to_string(argv[1]);
|
||||
int32_t slen = janet_string_length(str);
|
||||
size_t newcount = p->bufcount + slen;
|
||||
if (p->bufcap > p->bufcount + slen) {
|
||||
size_t newcap = 2 * newcount;
|
||||
p->buf = realloc(p->buf, newcap);
|
||||
if (p->buf == NULL) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
p->bufcap = newcap;
|
||||
}
|
||||
memcpy(p->buf + p->bufcount, str, slen);
|
||||
p->bufcount = newcount;
|
||||
} else {
|
||||
janet_panic("cannot insert value into parser");
|
||||
}
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static Janet cfun_parse_has_more(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetParser *p = janet_getabstract(argv, 0, &janet_parse_parsertype);
|
||||
return janet_wrap_boolean(janet_parser_has_more(p));
|
||||
}
|
||||
|
||||
static Janet cfun_byte(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_parse_byte(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 2);
|
||||
JanetParser *p = janet_getabstract(argv, 0, &janet_parse_parsertype);
|
||||
int32_t i = janet_getinteger(argv, 1);
|
||||
@@ -659,7 +700,7 @@ static Janet cfun_byte(int32_t argc, Janet *argv) {
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static Janet cfun_status(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_parse_status(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetParser *p = janet_getabstract(argv, 0, &janet_parse_parsertype);
|
||||
const char *stat = NULL;
|
||||
@@ -677,7 +718,7 @@ static Janet cfun_status(int32_t argc, Janet *argv) {
|
||||
return janet_ckeywordv(stat);
|
||||
}
|
||||
|
||||
static Janet cfun_error(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_parse_error(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetParser *p = janet_getabstract(argv, 0, &janet_parse_parsertype);
|
||||
const char *err = janet_parser_error(p);
|
||||
@@ -685,26 +726,26 @@ static Janet cfun_error(int32_t argc, Janet *argv) {
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
|
||||
static Janet cfun_produce(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_parse_produce(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetParser *p = janet_getabstract(argv, 0, &janet_parse_parsertype);
|
||||
return janet_parser_produce(p);
|
||||
}
|
||||
|
||||
static Janet cfun_flush(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_parse_flush(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetParser *p = janet_getabstract(argv, 0, &janet_parse_parsertype);
|
||||
janet_parser_flush(p);
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static Janet cfun_where(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_parse_where(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetParser *p = janet_getabstract(argv, 0, &janet_parse_parsertype);
|
||||
return janet_wrap_integer(p->offset);
|
||||
}
|
||||
|
||||
static Janet cfun_state(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_parse_state(int32_t argc, Janet *argv) {
|
||||
size_t i;
|
||||
const uint8_t *str;
|
||||
size_t oldcount;
|
||||
@@ -733,39 +774,59 @@ static Janet cfun_state(int32_t argc, Janet *argv) {
|
||||
return janet_wrap_string(str);
|
||||
}
|
||||
|
||||
static const JanetReg cfuns[] = {
|
||||
static const JanetMethod parser_methods[] = {
|
||||
{"byte", cfun_parse_byte},
|
||||
{"consume", cfun_parse_consume},
|
||||
{"error", cfun_parse_error},
|
||||
{"flush", cfun_parse_flush},
|
||||
{"has-more", cfun_parse_has_more},
|
||||
{"insert", cfun_parse_insert},
|
||||
{"produce", cfun_parse_produce},
|
||||
{"state", cfun_parse_state},
|
||||
{"status", cfun_parse_status},
|
||||
{"where", cfun_parse_where},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
static Janet parserget(void *p, Janet key) {
|
||||
(void) p;
|
||||
if (!janet_checktype(key, JANET_KEYWORD)) janet_panicf("expected keyword method");
|
||||
return janet_getmethod(janet_unwrap_keyword(key), parser_methods);
|
||||
}
|
||||
|
||||
static const JanetReg parse_cfuns[] = {
|
||||
{
|
||||
"parser/new", cfun_parser,
|
||||
"parser/new", cfun_parse_parser,
|
||||
JDOC("(parser/new)\n\n"
|
||||
"Creates and returns a new parser object. Parsers are state machines "
|
||||
"that can receive bytes, and generate a stream of janet values. ")
|
||||
},
|
||||
{
|
||||
"parser/has-more", cfun_has_more,
|
||||
"parser/has-more", cfun_parse_has_more,
|
||||
JDOC("(parser/has-more parser)\n\n"
|
||||
"Check if the parser has more values in the value queue.")
|
||||
},
|
||||
{
|
||||
"parser/produce", cfun_produce,
|
||||
"parser/produce", cfun_parse_produce,
|
||||
JDOC("(parser/produce parser)\n\n"
|
||||
"Dequeue the next value in the parse queue. Will return nil if "
|
||||
"no parsed values are in the queue, otherwise will dequeue the "
|
||||
"next value.")
|
||||
},
|
||||
{
|
||||
"parser/consume", cfun_consume,
|
||||
"parser/consume", cfun_parse_consume,
|
||||
JDOC("(parser/consume parser bytes [, index])\n\n"
|
||||
"Input bytes into the parser and parse them. Will not throw errors "
|
||||
"if there is a parse error. Starts at the byte index given by index. Returns "
|
||||
"the number of bytes read.")
|
||||
},
|
||||
{
|
||||
"parser/byte", cfun_byte,
|
||||
"parser/byte", cfun_parse_byte,
|
||||
JDOC("(parser/byte parser b)\n\n"
|
||||
"Input a single byte into the parser byte stream. Returns the parser.")
|
||||
},
|
||||
{
|
||||
"parser/error", cfun_error,
|
||||
"parser/error", cfun_parse_error,
|
||||
JDOC("(parser/error parser)\n\n"
|
||||
"If the parser is in the error state, returns the message associated with "
|
||||
"that error. Otherwise, returns nil. Also flushes the parser state and parser "
|
||||
@@ -773,7 +834,7 @@ static const JanetReg cfuns[] = {
|
||||
"parser/error.")
|
||||
},
|
||||
{
|
||||
"parser/status", cfun_status,
|
||||
"parser/status", cfun_parse_status,
|
||||
JDOC("(parser/status parser)\n\n"
|
||||
"Gets the current status of the parser state machine. The status will "
|
||||
"be one of:\n\n"
|
||||
@@ -782,14 +843,14 @@ static const JanetReg cfuns[] = {
|
||||
"\t:root - the parser can either read more values or safely terminate.")
|
||||
},
|
||||
{
|
||||
"parser/flush", cfun_flush,
|
||||
"parser/flush", cfun_parse_flush,
|
||||
JDOC("(parser/flush parser)\n\n"
|
||||
"Clears the parser state and parse queue. Can be used to reset the parser "
|
||||
"if an error was encountered. Does not reset the line and column counter, so "
|
||||
"to begin parsing in a new context, create a new parser.")
|
||||
},
|
||||
{
|
||||
"parser/state", cfun_state,
|
||||
"parser/state", cfun_parse_state,
|
||||
JDOC("(parser/state parser)\n\n"
|
||||
"Returns a string representation of the internal state of the parser. "
|
||||
"Each byte in the string represents a nested data structure. For example, "
|
||||
@@ -797,16 +858,23 @@ static const JanetReg cfuns[] = {
|
||||
"string inside of square brackets inside parentheses. Can be used to augment a REPL prompt.")
|
||||
},
|
||||
{
|
||||
"parser/where", cfun_where,
|
||||
"parser/where", cfun_parse_where,
|
||||
JDOC("(parser/where parser)\n\n"
|
||||
"Returns the current line number and column number of the parser's location "
|
||||
"in the byte stream as a tuple (line, column). Lines and columns are counted from "
|
||||
"1, (the first byte is line 1, column 1) and a newline is considered ASCII 0x0A.")
|
||||
},
|
||||
{
|
||||
"parser/insert", cfun_parse_insert,
|
||||
JDOC("(parser/insert parser value)\n\n"
|
||||
"Insert a value into the parser. This means that the parser state can be manipulated "
|
||||
"in between chunks of bytes. This would allow a user to add extra elements to arrays "
|
||||
"and tuples, for example. Returns the parser.")
|
||||
},
|
||||
{NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
/* Load the library */
|
||||
void janet_lib_parse(JanetTable *env) {
|
||||
janet_cfuns(env, NULL, cfuns);
|
||||
janet_core_cfuns(env, NULL, parse_cfuns);
|
||||
}
|
||||
|
||||
1107
src/core/peg.c
Normal file
1107
src/core/peg.c
Normal file
File diff suppressed because it is too large
Load Diff
548
src/core/pp.c
Normal file
548
src/core/pp.c
Normal file
@@ -0,0 +1,548 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet/janet.h>
|
||||
#include "util.h"
|
||||
#include "state.h"
|
||||
#endif
|
||||
|
||||
/* Implements a pretty printer for Janet. The pretty printer
|
||||
* is farily simple and not that flexible, but fast. */
|
||||
|
||||
/* Temporary buffer size */
|
||||
#define BUFSIZE 64
|
||||
|
||||
static void number_to_string_b(JanetBuffer *buffer, double x) {
|
||||
janet_buffer_ensure(buffer, buffer->count + BUFSIZE, 2);
|
||||
int count = snprintf((char *) buffer->data + buffer->count, BUFSIZE, "%g", x);
|
||||
buffer->count += count;
|
||||
}
|
||||
|
||||
/* expects non positive x */
|
||||
static int count_dig10(int32_t x) {
|
||||
int result = 1;
|
||||
for (;;) {
|
||||
if (x > -10) return result;
|
||||
if (x > -100) return result + 1;
|
||||
if (x > -1000) return result + 2;
|
||||
if (x > -10000) return result + 3;
|
||||
x /= 10000;
|
||||
result += 4;
|
||||
}
|
||||
}
|
||||
|
||||
static void integer_to_string_b(JanetBuffer *buffer, int32_t x) {
|
||||
janet_buffer_extra(buffer, BUFSIZE);
|
||||
uint8_t *buf = buffer->data + buffer->count;
|
||||
int32_t neg = 0;
|
||||
int32_t len = 0;
|
||||
if (x == 0) {
|
||||
buf[0] = '0';
|
||||
buffer->count++;
|
||||
return;
|
||||
}
|
||||
if (x > 0) {
|
||||
x = -x;
|
||||
} else {
|
||||
neg = 1;
|
||||
*buf++ = '-';
|
||||
}
|
||||
len = count_dig10(x);
|
||||
buf += len;
|
||||
while (x) {
|
||||
uint8_t digit = (uint8_t) -(x % 10);
|
||||
*(--buf) = '0' + digit;
|
||||
x /= 10;
|
||||
}
|
||||
buffer->count += len + neg;
|
||||
}
|
||||
|
||||
#define HEX(i) (((uint8_t *) janet_base64)[(i)])
|
||||
|
||||
/* Returns a string description for a pointer. Truncates
|
||||
* title to 32 characters */
|
||||
static void string_description_b(JanetBuffer *buffer, const char *title, void *pointer) {
|
||||
janet_buffer_ensure(buffer, buffer->count + BUFSIZE, 2);
|
||||
uint8_t *c = buffer->data + buffer->count;
|
||||
int32_t i;
|
||||
union {
|
||||
uint8_t bytes[sizeof(void *)];
|
||||
void *p;
|
||||
} pbuf;
|
||||
|
||||
pbuf.p = pointer;
|
||||
*c++ = '<';
|
||||
/* Maximum of 32 bytes for abstract type name */
|
||||
for (i = 0; title[i] && i < 32; ++i)
|
||||
*c++ = ((uint8_t *)title) [i];
|
||||
*c++ = ' ';
|
||||
*c++ = '0';
|
||||
*c++ = 'x';
|
||||
#if defined(JANET_64)
|
||||
#define POINTSIZE 6
|
||||
#else
|
||||
#define POINTSIZE (sizeof(void *))
|
||||
#endif
|
||||
for (i = POINTSIZE; i > 0; --i) {
|
||||
uint8_t byte = pbuf.bytes[i - 1];
|
||||
*c++ = HEX(byte >> 4);
|
||||
*c++ = HEX(byte & 0xF);
|
||||
}
|
||||
*c++ = '>';
|
||||
buffer->count = (int32_t)(c - buffer->data);
|
||||
#undef POINTSIZE
|
||||
}
|
||||
|
||||
#undef HEX
|
||||
#undef BUFSIZE
|
||||
|
||||
static void janet_escape_string_impl(JanetBuffer *buffer, const uint8_t *str, int32_t len) {
|
||||
janet_buffer_push_u8(buffer, '"');
|
||||
for (int32_t i = 0; i < len; ++i) {
|
||||
uint8_t c = str[i];
|
||||
switch (c) {
|
||||
case '"':
|
||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\\"", 2);
|
||||
break;
|
||||
case '\n':
|
||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\n", 2);
|
||||
break;
|
||||
case '\r':
|
||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\r", 2);
|
||||
break;
|
||||
case '\0':
|
||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\0", 2);
|
||||
break;
|
||||
case '\\':
|
||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\\\", 2);
|
||||
break;
|
||||
default:
|
||||
if (c < 32 || c > 127) {
|
||||
uint8_t buf[4];
|
||||
buf[0] = '\\';
|
||||
buf[1] = 'x';
|
||||
buf[2] = janet_base64[(c >> 4) & 0xF];
|
||||
buf[3] = janet_base64[c & 0xF];
|
||||
janet_buffer_push_bytes(buffer, buf, 4);
|
||||
} else {
|
||||
janet_buffer_push_u8(buffer, c);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
janet_buffer_push_u8(buffer, '"');
|
||||
}
|
||||
|
||||
static void janet_escape_string_b(JanetBuffer *buffer, const uint8_t *str) {
|
||||
janet_escape_string_impl(buffer, str, janet_string_length(str));
|
||||
}
|
||||
|
||||
static void janet_escape_buffer_b(JanetBuffer *buffer, JanetBuffer *bx) {
|
||||
janet_buffer_push_u8(buffer, '@');
|
||||
janet_escape_string_impl(buffer, bx->data, bx->count);
|
||||
}
|
||||
|
||||
void janet_description_b(JanetBuffer *buffer, Janet x) {
|
||||
switch (janet_type(x)) {
|
||||
case JANET_NIL:
|
||||
janet_buffer_push_cstring(buffer, "nil");
|
||||
return;
|
||||
case JANET_TRUE:
|
||||
janet_buffer_push_cstring(buffer, "true");
|
||||
return;
|
||||
case JANET_FALSE:
|
||||
janet_buffer_push_cstring(buffer, "false");
|
||||
return;
|
||||
case JANET_NUMBER:
|
||||
number_to_string_b(buffer, janet_unwrap_number(x));
|
||||
return;
|
||||
case JANET_KEYWORD:
|
||||
janet_buffer_push_u8(buffer, ':');
|
||||
/* fallthrough */
|
||||
case JANET_SYMBOL:
|
||||
janet_buffer_push_bytes(buffer,
|
||||
janet_unwrap_string(x),
|
||||
janet_string_length(janet_unwrap_string(x)));
|
||||
return;
|
||||
case JANET_STRING:
|
||||
janet_escape_string_b(buffer, janet_unwrap_string(x));
|
||||
return;
|
||||
case JANET_BUFFER:
|
||||
janet_escape_buffer_b(buffer, janet_unwrap_buffer(x));
|
||||
return;
|
||||
case JANET_ABSTRACT:
|
||||
{
|
||||
const char *n = janet_abstract_type(janet_unwrap_abstract(x))->name;
|
||||
string_description_b(buffer, n, janet_unwrap_abstract(x));
|
||||
return;
|
||||
}
|
||||
case JANET_CFUNCTION:
|
||||
{
|
||||
Janet check = janet_table_get(janet_vm_registry, x);
|
||||
if (janet_checktype(check, JANET_SYMBOL)) {
|
||||
janet_buffer_push_cstring(buffer, "<cfunction ");
|
||||
janet_buffer_push_bytes(buffer,
|
||||
janet_unwrap_symbol(check),
|
||||
janet_string_length(janet_unwrap_symbol(check)));
|
||||
janet_buffer_push_u8(buffer, '>');
|
||||
break;
|
||||
}
|
||||
goto fallthrough;
|
||||
}
|
||||
case JANET_FUNCTION:
|
||||
{
|
||||
JanetFunction *fun = janet_unwrap_function(x);
|
||||
JanetFuncDef *def = fun->def;
|
||||
if (def->name) {
|
||||
const uint8_t *n = def->name;
|
||||
janet_buffer_push_cstring(buffer, "<function ");
|
||||
janet_buffer_push_bytes(buffer, n, janet_string_length(n));
|
||||
janet_buffer_push_u8(buffer, '>');
|
||||
break;
|
||||
}
|
||||
goto fallthrough;
|
||||
}
|
||||
fallthrough:
|
||||
default:
|
||||
string_description_b(buffer, janet_type_names[janet_type(x)], janet_unwrap_pointer(x));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void janet_to_string_b(JanetBuffer *buffer, Janet x) {
|
||||
switch (janet_type(x)) {
|
||||
default:
|
||||
janet_description_b(buffer, x);
|
||||
break;
|
||||
case JANET_BUFFER:
|
||||
janet_buffer_push_bytes(buffer,
|
||||
janet_unwrap_buffer(x)->data,
|
||||
janet_unwrap_buffer(x)->count);
|
||||
break;
|
||||
case JANET_STRING:
|
||||
case JANET_SYMBOL:
|
||||
case JANET_KEYWORD:
|
||||
janet_buffer_push_bytes(buffer,
|
||||
janet_unwrap_string(x),
|
||||
janet_string_length(janet_unwrap_string(x)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const uint8_t *janet_description(Janet x) {
|
||||
JanetBuffer b;
|
||||
janet_buffer_init(&b, 10);
|
||||
janet_description_b(&b, x);
|
||||
const uint8_t *ret = janet_string(b.data, b.count);
|
||||
janet_buffer_deinit(&b);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Convert any value to a janet string. Similar to description, but
|
||||
* strings, symbols, and buffers will return their content. */
|
||||
const uint8_t *janet_to_string(Janet x) {
|
||||
switch (janet_type(x)) {
|
||||
default:
|
||||
{
|
||||
JanetBuffer b;
|
||||
janet_buffer_init(&b, 10);
|
||||
janet_to_string_b(&b, x);
|
||||
const uint8_t *ret = janet_string(b.data, b.count);
|
||||
janet_buffer_deinit(&b);
|
||||
return ret;
|
||||
}
|
||||
case JANET_BUFFER:
|
||||
return janet_string(janet_unwrap_buffer(x)->data, janet_unwrap_buffer(x)->count);
|
||||
case JANET_STRING:
|
||||
case JANET_SYMBOL:
|
||||
case JANET_KEYWORD:
|
||||
return janet_unwrap_string(x);
|
||||
}
|
||||
}
|
||||
|
||||
/* Hold state for pretty printer. */
|
||||
struct pretty {
|
||||
JanetBuffer *buffer;
|
||||
int depth;
|
||||
int indent;
|
||||
JanetTable seen;
|
||||
};
|
||||
|
||||
static void print_newline(struct pretty *S, int just_a_space) {
|
||||
int i;
|
||||
if (just_a_space) {
|
||||
janet_buffer_push_u8(S->buffer, ' ');
|
||||
return;
|
||||
}
|
||||
janet_buffer_push_u8(S->buffer, '\n');
|
||||
for (i = 0; i < S->indent; i++) {
|
||||
janet_buffer_push_u8(S->buffer, ' ');
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper for pretty printing */
|
||||
static void janet_pretty_one(struct pretty *S, Janet x, int is_dict_value) {
|
||||
/* Add to seen */
|
||||
switch (janet_type(x)) {
|
||||
case JANET_NIL:
|
||||
case JANET_NUMBER:
|
||||
case JANET_SYMBOL:
|
||||
case JANET_TRUE:
|
||||
case JANET_FALSE:
|
||||
break;
|
||||
default:
|
||||
{
|
||||
Janet seenid = janet_table_get(&S->seen, x);
|
||||
if (janet_checktype(seenid, JANET_NUMBER)) {
|
||||
janet_buffer_push_cstring(S->buffer, "<cycle ");
|
||||
integer_to_string_b(S->buffer, janet_unwrap_integer(seenid));
|
||||
janet_buffer_push_u8(S->buffer, '>');
|
||||
return;
|
||||
} else {
|
||||
janet_table_put(&S->seen, x, janet_wrap_integer(S->seen.count));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (janet_type(x)) {
|
||||
default:
|
||||
janet_description_b(S->buffer, x);
|
||||
break;
|
||||
case JANET_ARRAY:
|
||||
case JANET_TUPLE:
|
||||
{
|
||||
int isarray = janet_checktype(x, JANET_ARRAY);
|
||||
janet_buffer_push_cstring(S->buffer, isarray ? "@[" : "(");
|
||||
S->depth--;
|
||||
S->indent += 2;
|
||||
if (S->depth == 0) {
|
||||
janet_buffer_push_cstring(S->buffer, "...");
|
||||
} else {
|
||||
int32_t i, len;
|
||||
const Janet *arr;
|
||||
janet_indexed_view(x, &arr, &len);
|
||||
if (!isarray && len >= 5)
|
||||
janet_buffer_push_u8(S->buffer, ' ');
|
||||
if (is_dict_value && len >= 5) print_newline(S, 0);
|
||||
for (i = 0; i < len; i++) {
|
||||
if (i) print_newline(S, len < 5);
|
||||
janet_pretty_one(S, arr[i], 0);
|
||||
}
|
||||
}
|
||||
S->indent -= 2;
|
||||
S->depth++;
|
||||
janet_buffer_push_u8(S->buffer, isarray ? ']' : ')');
|
||||
break;
|
||||
}
|
||||
case JANET_STRUCT:
|
||||
case JANET_TABLE:
|
||||
{
|
||||
int istable = janet_checktype(x, JANET_TABLE);
|
||||
janet_buffer_push_cstring(S->buffer, istable ? "@" : "{");
|
||||
|
||||
/* For object-like tables, print class name */
|
||||
if (istable) {
|
||||
JanetTable *t = janet_unwrap_table(x);
|
||||
JanetTable *proto = t->proto;
|
||||
if (NULL != proto) {
|
||||
Janet name = janet_table_get(proto, janet_csymbolv(":name"));
|
||||
if (janet_checktype(name, JANET_SYMBOL)) {
|
||||
const uint8_t *sym = janet_unwrap_symbol(name);
|
||||
janet_buffer_push_bytes(S->buffer, sym, janet_string_length(sym));
|
||||
}
|
||||
}
|
||||
janet_buffer_push_cstring(S->buffer, "{");
|
||||
}
|
||||
|
||||
S->depth--;
|
||||
S->indent += 2;
|
||||
if (S->depth == 0) {
|
||||
janet_buffer_push_cstring(S->buffer, "...");
|
||||
} else {
|
||||
int32_t i, len, cap;
|
||||
int first_kv_pair = 1;
|
||||
const JanetKV *kvs;
|
||||
janet_dictionary_view(x, &kvs, &len, &cap);
|
||||
if (!istable && len >= 4)
|
||||
janet_buffer_push_u8(S->buffer, ' ');
|
||||
if (is_dict_value && len >= 5) print_newline(S, 0);
|
||||
for (i = 0; i < cap; i++) {
|
||||
if (!janet_checktype(kvs[i].key, JANET_NIL)) {
|
||||
if (first_kv_pair) {
|
||||
first_kv_pair = 0;
|
||||
} else {
|
||||
print_newline(S, len < 4);
|
||||
}
|
||||
janet_pretty_one(S, kvs[i].key, 0);
|
||||
janet_buffer_push_u8(S->buffer, ' ');
|
||||
janet_pretty_one(S, kvs[i].value, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
S->indent -= 2;
|
||||
S->depth++;
|
||||
janet_buffer_push_u8(S->buffer, '}');
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Remove from seen */
|
||||
janet_table_remove(&S->seen, x);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Helper for printing a janet value in a pretty form. Not meant to be used
|
||||
* for serialization or anything like that. */
|
||||
JanetBuffer *janet_pretty(JanetBuffer *buffer, int depth, Janet x) {
|
||||
struct pretty S;
|
||||
if (NULL == buffer) {
|
||||
buffer = janet_buffer(0);
|
||||
}
|
||||
S.buffer = buffer;
|
||||
S.depth = depth;
|
||||
S.indent = 0;
|
||||
janet_table_init(&S.seen, 10);
|
||||
janet_pretty_one(&S, x, 0);
|
||||
janet_table_deinit(&S.seen);
|
||||
return S.buffer;
|
||||
}
|
||||
|
||||
static const char *typestr(Janet x) {
|
||||
JanetType t = janet_type(x);
|
||||
return (t == JANET_ABSTRACT)
|
||||
? janet_abstract_type(janet_unwrap_abstract(x))->name
|
||||
: janet_type_names[t];
|
||||
}
|
||||
|
||||
static void pushtypes(JanetBuffer *buffer, int types) {
|
||||
int first = 1;
|
||||
int i = 0;
|
||||
while (types) {
|
||||
if (1 & types) {
|
||||
if (first) {
|
||||
first = 0;
|
||||
} else {
|
||||
janet_buffer_push_u8(buffer, '|');
|
||||
}
|
||||
janet_buffer_push_cstring(buffer, janet_type_names[i]);
|
||||
}
|
||||
i++;
|
||||
types >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper function for formatting strings. Useful for generating error messages and the like.
|
||||
* Similar to printf, but specialized for operating with janet. */
|
||||
const uint8_t *janet_formatc(const char *format, ...) {
|
||||
va_list args;
|
||||
int32_t len = 0;
|
||||
int32_t i;
|
||||
const uint8_t *ret;
|
||||
JanetBuffer buffer;
|
||||
JanetBuffer *bufp = &buffer;
|
||||
|
||||
/* Calculate length */
|
||||
while (format[len]) len++;
|
||||
|
||||
/* Initialize buffer */
|
||||
janet_buffer_init(bufp, len);
|
||||
|
||||
/* Start args */
|
||||
va_start(args, format);
|
||||
|
||||
/* Iterate length */
|
||||
for (i = 0; i < len; i++) {
|
||||
uint8_t c = format[i];
|
||||
switch (c) {
|
||||
default:
|
||||
janet_buffer_push_u8(bufp, c);
|
||||
break;
|
||||
case '%':
|
||||
{
|
||||
if (i + 1 >= len)
|
||||
break;
|
||||
switch (format[++i]) {
|
||||
default:
|
||||
janet_buffer_push_u8(bufp, format[i]);
|
||||
break;
|
||||
case 'f':
|
||||
number_to_string_b(bufp, va_arg(args, double));
|
||||
break;
|
||||
case 'd':
|
||||
integer_to_string_b(bufp, va_arg(args, long));
|
||||
break;
|
||||
case 'S':
|
||||
{
|
||||
const uint8_t *str = va_arg(args, const uint8_t *);
|
||||
janet_buffer_push_bytes(bufp, str, janet_string_length(str));
|
||||
break;
|
||||
}
|
||||
case 's':
|
||||
janet_buffer_push_cstring(bufp, va_arg(args, const char *));
|
||||
break;
|
||||
case 'c':
|
||||
janet_buffer_push_u8(bufp, (uint8_t) va_arg(args, long));
|
||||
break;
|
||||
case 'q':
|
||||
{
|
||||
const uint8_t *str = va_arg(args, const uint8_t *);
|
||||
janet_escape_string_b(bufp, str);
|
||||
break;
|
||||
}
|
||||
case 't':
|
||||
{
|
||||
janet_buffer_push_cstring(bufp, typestr(va_arg(args, Janet)));
|
||||
break;
|
||||
}
|
||||
case 'T':
|
||||
{
|
||||
int types = va_arg(args, long);
|
||||
pushtypes(bufp, types);
|
||||
break;
|
||||
}
|
||||
case 'V':
|
||||
{
|
||||
janet_to_string_b(bufp, va_arg(args, Janet));
|
||||
break;
|
||||
}
|
||||
case 'v':
|
||||
{
|
||||
janet_description_b(bufp, va_arg(args, Janet));
|
||||
break;
|
||||
}
|
||||
case 'p':
|
||||
{
|
||||
janet_pretty(bufp, 4, va_arg(args, Janet));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
|
||||
ret = janet_string(buffer.data, buffer.count);
|
||||
janet_buffer_deinit(&buffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -20,8 +20,10 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet/janet.h>
|
||||
#include "regalloc.h"
|
||||
#endif
|
||||
|
||||
void janetc_regalloc_init(JanetcRegisterAllocator *ra) {
|
||||
ra->chunks = NULL;
|
||||
|
||||
@@ -20,64 +20,10 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet/janet.h>
|
||||
#include "state.h"
|
||||
#include "vector.h"
|
||||
|
||||
/* Error reporting */
|
||||
void janet_stacktrace(JanetFiber *fiber, const char *errtype, Janet err) {
|
||||
int32_t fi;
|
||||
const char *errstr = (const char *)janet_to_string(err);
|
||||
JanetFiber **fibers = NULL;
|
||||
fprintf(stderr, "%s error: %s\n", errtype, errstr);
|
||||
|
||||
while (fiber) {
|
||||
janet_v_push(fibers, fiber);
|
||||
fiber = fiber->child;
|
||||
}
|
||||
|
||||
for (fi = janet_v_count(fibers) - 1; fi >= 0; fi--) {
|
||||
fiber = fibers[fi];
|
||||
int32_t i = fiber->frame;
|
||||
if (i > 0) fprintf(stderr, " (fiber)\n");
|
||||
while (i > 0) {
|
||||
JanetStackFrame *frame = (JanetStackFrame *)(fiber->data + i - JANET_FRAME_SIZE);
|
||||
JanetFuncDef *def = NULL;
|
||||
i = frame->prevframe;
|
||||
fprintf(stderr, " in");
|
||||
if (frame->func) {
|
||||
def = frame->func->def;
|
||||
fprintf(stderr, " %s", def->name ? (const char *)def->name : "<anonymous>");
|
||||
if (def->source) {
|
||||
fprintf(stderr, " [%s]", (const char *)def->source);
|
||||
}
|
||||
} else {
|
||||
JanetCFunction cfun = (JanetCFunction)(frame->pc);
|
||||
if (cfun) {
|
||||
Janet name = janet_table_get(janet_vm_registry, janet_wrap_cfunction(cfun));
|
||||
if (!janet_checktype(name, JANET_NIL))
|
||||
fprintf(stderr, " %s", (const char *)janet_to_string(name));
|
||||
else
|
||||
fprintf(stderr, " <cfunction>");
|
||||
}
|
||||
}
|
||||
if (frame->flags & JANET_STACKFRAME_TAILCALL)
|
||||
fprintf(stderr, " (tailcall)");
|
||||
if (frame->func && frame->pc) {
|
||||
int32_t off = (int32_t) (frame->pc - def->bytecode);
|
||||
if (def->sourcemap) {
|
||||
JanetSourceMapping mapping = def->sourcemap[off];
|
||||
fprintf(stderr, " at (%d:%d)", mapping.start, mapping.end);
|
||||
} else {
|
||||
fprintf(stderr, " pc=%d", off);
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
janet_v_free(fibers);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Run a string */
|
||||
int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char *sourcePath, Janet *out) {
|
||||
@@ -89,6 +35,7 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
|
||||
Janet ret = janet_wrap_nil();
|
||||
const uint8_t *where = sourcePath ? janet_cstring(sourcePath) : NULL;
|
||||
if (where) janet_gcroot(janet_wrap_string(where));
|
||||
if (NULL == sourcePath) sourcePath = "<unknown>";
|
||||
janet_parser_init(&parser);
|
||||
|
||||
while (!errflags && !done) {
|
||||
@@ -99,16 +46,15 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
|
||||
JanetCompileResult cres = janet_compile(form, env, where);
|
||||
if (cres.status == JANET_COMPILE_OK) {
|
||||
JanetFunction *f = janet_thunk(cres.funcdef);
|
||||
JanetFiber *fiber = janet_fiber(f, 64);
|
||||
JanetFiber *fiber = janet_fiber(f, 64, 0, NULL);
|
||||
JanetSignal status = janet_continue(fiber, janet_wrap_nil(), &ret);
|
||||
if (status != JANET_SIGNAL_OK) {
|
||||
janet_stacktrace(fiber, "runtime", ret);
|
||||
janet_stacktrace(fiber, ret);
|
||||
errflags |= 0x01;
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "source path: %s\n", sourcePath);
|
||||
janet_stacktrace(cres.macrofiber, "compile",
|
||||
janet_wrap_string(cres.error));
|
||||
fprintf(stderr, "compile error in %s: %s\n", sourcePath,
|
||||
(const char *)cres.error);
|
||||
errflags |= 0x02;
|
||||
}
|
||||
}
|
||||
@@ -117,13 +63,15 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
|
||||
switch (janet_parser_status(&parser)) {
|
||||
case JANET_PARSE_ERROR:
|
||||
errflags |= 0x04;
|
||||
fprintf(stderr, "parse error: %s\n", janet_parser_error(&parser));
|
||||
fprintf(stderr, "parse error in %s: %s\n",
|
||||
sourcePath, janet_parser_error(&parser));
|
||||
break;
|
||||
case JANET_PARSE_PENDING:
|
||||
if (index >= len) {
|
||||
if (dudeol) {
|
||||
errflags |= 0x04;
|
||||
fprintf(stderr, "internal parse error: unexpected end of source\n");
|
||||
fprintf(stderr, "internal parse error in %s: unexpected end of source\n",
|
||||
sourcePath);
|
||||
} else {
|
||||
dudeol = 1;
|
||||
janet_parser_consume(&parser, '\n');
|
||||
|
||||
@@ -20,11 +20,13 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet/janet.h>
|
||||
#include "compile.h"
|
||||
#include "util.h"
|
||||
#include "vector.h"
|
||||
#include "emit.h"
|
||||
#endif
|
||||
|
||||
static JanetSlot janetc_quote(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||
if (argn != 1) {
|
||||
@@ -219,7 +221,6 @@ static JanetSlot janetc_varset(JanetFopts opts, int32_t argn, const Janet *argv)
|
||||
return rvalue;
|
||||
} else {
|
||||
/* Error */
|
||||
janet_inspect(argv[0]);
|
||||
janetc_cerror(opts.compiler, "expected symbol or tuple for l-value to set");
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
}
|
||||
|
||||
@@ -39,6 +39,11 @@ extern JANET_THREAD_LOCAL int janet_vm_stackn;
|
||||
* Set and unset by janet_run. */
|
||||
extern JANET_THREAD_LOCAL JanetFiber *janet_vm_fiber;
|
||||
|
||||
/* The current pointer to the inner most jmp_buf. The current
|
||||
* return point for panics. */
|
||||
extern JANET_THREAD_LOCAL jmp_buf *janet_vm_jmp_buf;
|
||||
extern JANET_THREAD_LOCAL Janet *janet_vm_return_reg;
|
||||
|
||||
/* The global registry for c functions. Used to store meta-data
|
||||
* along with otherwise bare c function pointers. */
|
||||
extern JANET_THREAD_LOCAL JanetTable *janet_vm_registry;
|
||||
|
||||
@@ -20,10 +20,14 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet/janet.h>
|
||||
#include "gc.h"
|
||||
#include "util.h"
|
||||
#include "state.h"
|
||||
#endif
|
||||
|
||||
/* Begin building a string */
|
||||
uint8_t *janet_string_begin(int32_t length) {
|
||||
@@ -57,37 +61,21 @@ int janet_string_compare(const uint8_t *lhs, const uint8_t *rhs) {
|
||||
int32_t xlen = janet_string_length(lhs);
|
||||
int32_t ylen = janet_string_length(rhs);
|
||||
int32_t len = xlen > ylen ? ylen : xlen;
|
||||
int32_t i;
|
||||
for (i = 0; i < len; ++i) {
|
||||
if (lhs[i] == rhs[i]) {
|
||||
continue;
|
||||
} else if (lhs[i] < rhs[i]) {
|
||||
return -1; /* x is less than y */
|
||||
} else {
|
||||
return 1; /* y is less than x */
|
||||
}
|
||||
}
|
||||
if (xlen == ylen) {
|
||||
return 0;
|
||||
} else {
|
||||
return xlen < ylen ? -1 : 1;
|
||||
}
|
||||
int res = memcmp(lhs, rhs, len);
|
||||
if (res) return res;
|
||||
if (xlen == ylen) return 0;
|
||||
return xlen < ylen ? -1 : 1;
|
||||
}
|
||||
|
||||
/* Compare a janet string with a piece of memory */
|
||||
int janet_string_equalconst(const uint8_t *lhs, const uint8_t *rhs, int32_t rlen, int32_t rhash) {
|
||||
int32_t index;
|
||||
int32_t lhash = janet_string_hash(lhs);
|
||||
int32_t llen = janet_string_length(lhs);
|
||||
if (lhs == rhs)
|
||||
return 1;
|
||||
if (lhash != rhash || llen != rlen)
|
||||
return 0;
|
||||
for (index = 0; index < llen; index++) {
|
||||
if (lhs[index] != rhs[index])
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
return !memcmp(lhs, rhs, rlen);
|
||||
}
|
||||
|
||||
/* Check if two strings are equal */
|
||||
@@ -98,535 +86,7 @@ int janet_string_equal(const uint8_t *lhs, const uint8_t *rhs) {
|
||||
|
||||
/* Load a c string */
|
||||
const uint8_t *janet_cstring(const char *str) {
|
||||
int32_t len = 0;
|
||||
while (str[len]) ++len;
|
||||
return janet_string((const uint8_t *)str, len);
|
||||
}
|
||||
|
||||
/* Temporary buffer size */
|
||||
#define BUFSIZE 64
|
||||
|
||||
static void number_to_string_b(JanetBuffer *buffer, double x) {
|
||||
janet_buffer_ensure(buffer, buffer->count + BUFSIZE, 2);
|
||||
int count = snprintf((char *) buffer->data + buffer->count, BUFSIZE, "%g", x);
|
||||
buffer->count += count;
|
||||
}
|
||||
|
||||
/* expects non positive x */
|
||||
static int count_dig10(int32_t x) {
|
||||
int result = 1;
|
||||
for (;;) {
|
||||
if (x > -10) return result;
|
||||
if (x > -100) return result + 1;
|
||||
if (x > -1000) return result + 2;
|
||||
if (x > -10000) return result + 3;
|
||||
x /= 10000;
|
||||
result += 4;
|
||||
}
|
||||
}
|
||||
|
||||
static void integer_to_string_b(JanetBuffer *buffer, int32_t x) {
|
||||
janet_buffer_extra(buffer, BUFSIZE);
|
||||
uint8_t *buf = buffer->data + buffer->count;
|
||||
int32_t neg = 0;
|
||||
int32_t len = 0;
|
||||
if (x == 0) {
|
||||
buf[0] = '0';
|
||||
buffer->count++;
|
||||
return;
|
||||
}
|
||||
if (x > 0) {
|
||||
x = -x;
|
||||
} else {
|
||||
neg = 1;
|
||||
*buf++ = '-';
|
||||
}
|
||||
len = count_dig10(x);
|
||||
buf += len;
|
||||
while (x) {
|
||||
uint8_t digit = (uint8_t) -(x % 10);
|
||||
*(--buf) = '0' + digit;
|
||||
x /= 10;
|
||||
}
|
||||
buffer->count += len + neg;
|
||||
}
|
||||
|
||||
#define HEX(i) (((uint8_t *) janet_base64)[(i)])
|
||||
|
||||
/* Returns a string description for a pointer. Truncates
|
||||
* title to 32 characters */
|
||||
static void string_description_b(JanetBuffer *buffer, const char *title, void *pointer) {
|
||||
janet_buffer_ensure(buffer, buffer->count + BUFSIZE, 2);
|
||||
uint8_t *c = buffer->data + buffer->count;
|
||||
int32_t i;
|
||||
union {
|
||||
uint8_t bytes[sizeof(void *)];
|
||||
void *p;
|
||||
} pbuf;
|
||||
|
||||
pbuf.p = pointer;
|
||||
*c++ = '<';
|
||||
/* Maximum of 32 bytes for abstract type name */
|
||||
for (i = 0; title[i] && i < 32; ++i)
|
||||
*c++ = ((uint8_t *)title) [i];
|
||||
*c++ = ' ';
|
||||
*c++ = '0';
|
||||
*c++ = 'x';
|
||||
#if defined(JANET_64)
|
||||
#define POINTSIZE 6
|
||||
#else
|
||||
#define POINTSIZE (sizeof(void *))
|
||||
#endif
|
||||
for (i = POINTSIZE; i > 0; --i) {
|
||||
uint8_t byte = pbuf.bytes[i - 1];
|
||||
*c++ = HEX(byte >> 4);
|
||||
*c++ = HEX(byte & 0xF);
|
||||
}
|
||||
*c++ = '>';
|
||||
buffer->count = (int32_t)(c - buffer->data);
|
||||
#undef POINTSIZE
|
||||
}
|
||||
|
||||
#undef HEX
|
||||
#undef BUFSIZE
|
||||
|
||||
static void janet_escape_string_impl(JanetBuffer *buffer, const uint8_t *str, int32_t len) {
|
||||
janet_buffer_push_u8(buffer, '"');
|
||||
for (int32_t i = 0; i < len; ++i) {
|
||||
uint8_t c = str[i];
|
||||
switch (c) {
|
||||
case '"':
|
||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\\"", 2);
|
||||
break;
|
||||
case '\n':
|
||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\n", 2);
|
||||
break;
|
||||
case '\r':
|
||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\r", 2);
|
||||
break;
|
||||
case '\0':
|
||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\0", 2);
|
||||
break;
|
||||
case '\\':
|
||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\\\", 2);
|
||||
break;
|
||||
default:
|
||||
if (c < 32 || c > 127) {
|
||||
uint8_t buf[4];
|
||||
buf[0] = '\\';
|
||||
buf[1] = 'x';
|
||||
buf[2] = janet_base64[(c >> 4) & 0xF];
|
||||
buf[3] = janet_base64[c & 0xF];
|
||||
janet_buffer_push_bytes(buffer, buf, 4);
|
||||
} else {
|
||||
janet_buffer_push_u8(buffer, c);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
janet_buffer_push_u8(buffer, '"');
|
||||
}
|
||||
|
||||
static void janet_escape_string_b(JanetBuffer *buffer, const uint8_t *str) {
|
||||
janet_escape_string_impl(buffer, str, janet_string_length(str));
|
||||
}
|
||||
|
||||
static void janet_escape_buffer_b(JanetBuffer *buffer, JanetBuffer *bx) {
|
||||
janet_buffer_push_u8(buffer, '@');
|
||||
janet_escape_string_impl(buffer, bx->data, bx->count);
|
||||
}
|
||||
|
||||
void janet_description_b(JanetBuffer *buffer, Janet x) {
|
||||
switch (janet_type(x)) {
|
||||
case JANET_NIL:
|
||||
janet_buffer_push_cstring(buffer, "nil");
|
||||
return;
|
||||
case JANET_TRUE:
|
||||
janet_buffer_push_cstring(buffer, "true");
|
||||
return;
|
||||
case JANET_FALSE:
|
||||
janet_buffer_push_cstring(buffer, "false");
|
||||
return;
|
||||
case JANET_NUMBER:
|
||||
number_to_string_b(buffer, janet_unwrap_number(x));
|
||||
return;
|
||||
case JANET_KEYWORD:
|
||||
janet_buffer_push_u8(buffer, ':');
|
||||
/* fallthrough */
|
||||
case JANET_SYMBOL:
|
||||
janet_buffer_push_bytes(buffer,
|
||||
janet_unwrap_string(x),
|
||||
janet_string_length(janet_unwrap_string(x)));
|
||||
return;
|
||||
case JANET_STRING:
|
||||
janet_escape_string_b(buffer, janet_unwrap_string(x));
|
||||
return;
|
||||
case JANET_BUFFER:
|
||||
janet_escape_buffer_b(buffer, janet_unwrap_buffer(x));
|
||||
return;
|
||||
case JANET_ABSTRACT:
|
||||
{
|
||||
const char *n = janet_abstract_type(janet_unwrap_abstract(x))->name;
|
||||
string_description_b(buffer, n, janet_unwrap_abstract(x));
|
||||
return;
|
||||
}
|
||||
case JANET_CFUNCTION:
|
||||
{
|
||||
Janet check = janet_table_get(janet_vm_registry, x);
|
||||
if (janet_checktype(check, JANET_SYMBOL)) {
|
||||
janet_buffer_push_cstring(buffer, "<cfunction ");
|
||||
janet_buffer_push_bytes(buffer,
|
||||
janet_unwrap_symbol(check),
|
||||
janet_string_length(janet_unwrap_symbol(check)));
|
||||
janet_buffer_push_u8(buffer, '>');
|
||||
break;
|
||||
}
|
||||
goto fallthrough;
|
||||
}
|
||||
case JANET_FUNCTION:
|
||||
{
|
||||
JanetFunction *fun = janet_unwrap_function(x);
|
||||
JanetFuncDef *def = fun->def;
|
||||
if (def->name) {
|
||||
const uint8_t *n = def->name;
|
||||
janet_buffer_push_cstring(buffer, "<function ");
|
||||
janet_buffer_push_bytes(buffer, n, janet_string_length(n));
|
||||
janet_buffer_push_u8(buffer, '>');
|
||||
break;
|
||||
}
|
||||
goto fallthrough;
|
||||
}
|
||||
fallthrough:
|
||||
default:
|
||||
string_description_b(buffer, janet_type_names[janet_type(x)], janet_unwrap_pointer(x));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void janet_to_string_b(JanetBuffer *buffer, Janet x) {
|
||||
switch (janet_type(x)) {
|
||||
default:
|
||||
janet_description_b(buffer, x);
|
||||
break;
|
||||
case JANET_BUFFER:
|
||||
janet_buffer_push_bytes(buffer,
|
||||
janet_unwrap_buffer(x)->data,
|
||||
janet_unwrap_buffer(x)->count);
|
||||
break;
|
||||
case JANET_STRING:
|
||||
case JANET_SYMBOL:
|
||||
case JANET_KEYWORD:
|
||||
janet_buffer_push_bytes(buffer,
|
||||
janet_unwrap_string(x),
|
||||
janet_string_length(janet_unwrap_string(x)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const uint8_t *janet_description(Janet x) {
|
||||
JanetBuffer b;
|
||||
janet_buffer_init(&b, 10);
|
||||
janet_description_b(&b, x);
|
||||
const uint8_t *ret = janet_string(b.data, b.count);
|
||||
janet_buffer_deinit(&b);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Convert any value to a janet string. Similar to description, but
|
||||
* strings, symbols, and buffers will return their content. */
|
||||
const uint8_t *janet_to_string(Janet x) {
|
||||
switch (janet_type(x)) {
|
||||
default:
|
||||
{
|
||||
JanetBuffer b;
|
||||
janet_buffer_init(&b, 10);
|
||||
janet_to_string_b(&b, x);
|
||||
const uint8_t *ret = janet_string(b.data, b.count);
|
||||
janet_buffer_deinit(&b);
|
||||
return ret;
|
||||
}
|
||||
case JANET_BUFFER:
|
||||
return janet_string(janet_unwrap_buffer(x)->data, janet_unwrap_buffer(x)->count);
|
||||
case JANET_STRING:
|
||||
case JANET_SYMBOL:
|
||||
case JANET_KEYWORD:
|
||||
return janet_unwrap_string(x);
|
||||
}
|
||||
}
|
||||
|
||||
/* Hold state for pretty printer. */
|
||||
struct pretty {
|
||||
JanetBuffer *buffer;
|
||||
int depth;
|
||||
int indent;
|
||||
JanetTable seen;
|
||||
};
|
||||
|
||||
static void print_newline(struct pretty *S, int just_a_space) {
|
||||
int i;
|
||||
if (just_a_space) {
|
||||
janet_buffer_push_u8(S->buffer, ' ');
|
||||
return;
|
||||
}
|
||||
janet_buffer_push_u8(S->buffer, '\n');
|
||||
for (i = 0; i < S->indent; i++) {
|
||||
janet_buffer_push_u8(S->buffer, ' ');
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper for pretty printing */
|
||||
static void janet_pretty_one(struct pretty *S, Janet x, int is_dict_value) {
|
||||
/* Add to seen */
|
||||
switch (janet_type(x)) {
|
||||
case JANET_NIL:
|
||||
case JANET_NUMBER:
|
||||
case JANET_SYMBOL:
|
||||
case JANET_TRUE:
|
||||
case JANET_FALSE:
|
||||
break;
|
||||
default:
|
||||
{
|
||||
Janet seenid = janet_table_get(&S->seen, x);
|
||||
if (janet_checktype(seenid, JANET_NUMBER)) {
|
||||
janet_buffer_push_cstring(S->buffer, "<cycle ");
|
||||
integer_to_string_b(S->buffer, janet_unwrap_integer(seenid));
|
||||
janet_buffer_push_u8(S->buffer, '>');
|
||||
return;
|
||||
} else {
|
||||
janet_table_put(&S->seen, x, janet_wrap_integer(S->seen.count));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (janet_type(x)) {
|
||||
default:
|
||||
janet_description_b(S->buffer, x);
|
||||
break;
|
||||
case JANET_ARRAY:
|
||||
case JANET_TUPLE:
|
||||
{
|
||||
int isarray = janet_checktype(x, JANET_ARRAY);
|
||||
janet_buffer_push_cstring(S->buffer, isarray ? "@[" : "(");
|
||||
S->depth--;
|
||||
S->indent += 2;
|
||||
if (S->depth == 0) {
|
||||
janet_buffer_push_cstring(S->buffer, "...");
|
||||
} else {
|
||||
int32_t i, len;
|
||||
const Janet *arr;
|
||||
janet_indexed_view(x, &arr, &len);
|
||||
if (!isarray && len >= 5)
|
||||
janet_buffer_push_u8(S->buffer, ' ');
|
||||
if (is_dict_value && len >= 5) print_newline(S, 0);
|
||||
for (i = 0; i < len; i++) {
|
||||
if (i) print_newline(S, len < 5);
|
||||
janet_pretty_one(S, arr[i], 0);
|
||||
}
|
||||
}
|
||||
S->indent -= 2;
|
||||
S->depth++;
|
||||
janet_buffer_push_u8(S->buffer, isarray ? ']' : ')');
|
||||
break;
|
||||
}
|
||||
case JANET_STRUCT:
|
||||
case JANET_TABLE:
|
||||
{
|
||||
int istable = janet_checktype(x, JANET_TABLE);
|
||||
janet_buffer_push_cstring(S->buffer, istable ? "@" : "{");
|
||||
|
||||
/* For object-like tables, print class name */
|
||||
if (istable) {
|
||||
JanetTable *t = janet_unwrap_table(x);
|
||||
JanetTable *proto = t->proto;
|
||||
if (NULL != proto) {
|
||||
Janet name = janet_table_get(proto, janet_csymbolv(":name"));
|
||||
if (janet_checktype(name, JANET_SYMBOL)) {
|
||||
const uint8_t *sym = janet_unwrap_symbol(name);
|
||||
janet_buffer_push_bytes(S->buffer, sym, janet_string_length(sym));
|
||||
}
|
||||
}
|
||||
janet_buffer_push_cstring(S->buffer, "{");
|
||||
}
|
||||
|
||||
S->depth--;
|
||||
S->indent += 2;
|
||||
if (S->depth == 0) {
|
||||
janet_buffer_push_cstring(S->buffer, "...");
|
||||
} else {
|
||||
int32_t i, len, cap;
|
||||
int first_kv_pair = 1;
|
||||
const JanetKV *kvs;
|
||||
janet_dictionary_view(x, &kvs, &len, &cap);
|
||||
if (!istable && len >= 4)
|
||||
janet_buffer_push_u8(S->buffer, ' ');
|
||||
if (is_dict_value && len >= 5) print_newline(S, 0);
|
||||
for (i = 0; i < cap; i++) {
|
||||
if (!janet_checktype(kvs[i].key, JANET_NIL)) {
|
||||
if (first_kv_pair) {
|
||||
first_kv_pair = 0;
|
||||
} else {
|
||||
print_newline(S, len < 4);
|
||||
}
|
||||
janet_pretty_one(S, kvs[i].key, 0);
|
||||
janet_buffer_push_u8(S->buffer, ' ');
|
||||
janet_pretty_one(S, kvs[i].value, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
S->indent -= 2;
|
||||
S->depth++;
|
||||
janet_buffer_push_u8(S->buffer, '}');
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Remove from seen */
|
||||
janet_table_remove(&S->seen, x);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Helper for printing a janet value in a pretty form. Not meant to be used
|
||||
* for serialization or anything like that. */
|
||||
JanetBuffer *janet_pretty(JanetBuffer *buffer, int depth, Janet x) {
|
||||
struct pretty S;
|
||||
if (NULL == buffer) {
|
||||
buffer = janet_buffer(0);
|
||||
}
|
||||
S.buffer = buffer;
|
||||
S.depth = depth;
|
||||
S.indent = 0;
|
||||
janet_table_init(&S.seen, 10);
|
||||
janet_pretty_one(&S, x, 0);
|
||||
janet_table_deinit(&S.seen);
|
||||
return S.buffer;
|
||||
}
|
||||
|
||||
static const char *typestr(Janet x) {
|
||||
JanetType t = janet_type(x);
|
||||
return (t = JANET_ABSTRACT)
|
||||
? janet_abstract_type(janet_unwrap_abstract(x))->name
|
||||
: janet_type_names[t];
|
||||
}
|
||||
|
||||
static void pushtypes(JanetBuffer *buffer, int types) {
|
||||
int first = 1;
|
||||
int i = 0;
|
||||
while (types) {
|
||||
if (1 & types) {
|
||||
if (first) {
|
||||
first = 0;
|
||||
} else {
|
||||
janet_buffer_push_u8(buffer, '|');
|
||||
}
|
||||
janet_buffer_push_cstring(buffer, janet_type_names[i]);
|
||||
}
|
||||
i++;
|
||||
types >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper function for formatting strings. Useful for generating error messages and the like.
|
||||
* Similar to printf, but specialized for operating with janet. */
|
||||
const uint8_t *janet_formatc(const char *format, ...) {
|
||||
va_list args;
|
||||
int32_t len = 0;
|
||||
int32_t i;
|
||||
const uint8_t *ret;
|
||||
JanetBuffer buffer;
|
||||
JanetBuffer *bufp = &buffer;
|
||||
|
||||
/* Calculate length */
|
||||
while (format[len]) len++;
|
||||
|
||||
/* Initialize buffer */
|
||||
janet_buffer_init(bufp, len);
|
||||
|
||||
/* Start args */
|
||||
va_start(args, format);
|
||||
|
||||
/* Iterate length */
|
||||
for (i = 0; i < len; i++) {
|
||||
uint8_t c = format[i];
|
||||
switch (c) {
|
||||
default:
|
||||
janet_buffer_push_u8(bufp, c);
|
||||
break;
|
||||
case '%':
|
||||
{
|
||||
if (i + 1 >= len)
|
||||
break;
|
||||
switch (format[++i]) {
|
||||
default:
|
||||
janet_buffer_push_u8(bufp, format[i]);
|
||||
break;
|
||||
case 'f':
|
||||
number_to_string_b(bufp, va_arg(args, double));
|
||||
break;
|
||||
case 'd':
|
||||
integer_to_string_b(bufp, va_arg(args, long));
|
||||
break;
|
||||
case 'S':
|
||||
{
|
||||
const uint8_t *str = va_arg(args, const uint8_t *);
|
||||
janet_buffer_push_bytes(bufp, str, janet_string_length(str));
|
||||
break;
|
||||
}
|
||||
case 's':
|
||||
janet_buffer_push_cstring(bufp, va_arg(args, const char *));
|
||||
break;
|
||||
case 'c':
|
||||
janet_buffer_push_u8(bufp, (uint8_t) va_arg(args, long));
|
||||
break;
|
||||
case 'q':
|
||||
{
|
||||
const uint8_t *str = va_arg(args, const uint8_t *);
|
||||
janet_escape_string_b(bufp, str);
|
||||
break;
|
||||
}
|
||||
case 't':
|
||||
{
|
||||
janet_buffer_push_cstring(bufp, typestr(va_arg(args, Janet)));
|
||||
break;
|
||||
}
|
||||
case 'T':
|
||||
{
|
||||
int types = va_arg(args, long);
|
||||
pushtypes(bufp, types);
|
||||
break;
|
||||
}
|
||||
case 'V':
|
||||
{
|
||||
janet_to_string_b(bufp, va_arg(args, Janet));
|
||||
break;
|
||||
}
|
||||
case 'v':
|
||||
{
|
||||
janet_description_b(bufp, va_arg(args, Janet));
|
||||
break;
|
||||
}
|
||||
case 'p':
|
||||
{
|
||||
janet_pretty(bufp, 4, va_arg(args, Janet));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
|
||||
ret = janet_string(buffer.data, buffer.count);
|
||||
janet_buffer_deinit(&buffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Print string to stdout */
|
||||
void janet_puts(const uint8_t *str) {
|
||||
int32_t i;
|
||||
int32_t len = janet_string_length(str);
|
||||
for (i = 0; i < len; i++) {
|
||||
putc(str[i], stdout);
|
||||
}
|
||||
return janet_string((const uint8_t *)str, (int32_t)strlen(str));
|
||||
}
|
||||
|
||||
/* Knuth Morris Pratt Algorithm */
|
||||
@@ -707,13 +167,13 @@ static int32_t kmp_next(struct kmp_state *state) {
|
||||
|
||||
/* CFuns */
|
||||
|
||||
static Janet cfun_slice(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_string_slice(int32_t argc, Janet *argv) {
|
||||
JanetRange range = janet_getslice(argc, argv);
|
||||
JanetByteView view = janet_getbytes(argv, 0);
|
||||
return janet_stringv(view.bytes + range.start, range.end - range.start);
|
||||
}
|
||||
|
||||
static Janet cfun_repeat(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_string_repeat(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 2);
|
||||
JanetByteView view = janet_getbytes(argv, 0);
|
||||
int32_t rep = janet_getinteger(argv, 1);
|
||||
@@ -730,7 +190,7 @@ static Janet cfun_repeat(int32_t argc, Janet *argv) {
|
||||
return janet_wrap_string(janet_string_end(newbuf));
|
||||
}
|
||||
|
||||
static Janet cfun_bytes(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_string_bytes(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetByteView view = janet_getbytes(argv, 0);
|
||||
Janet *tup = janet_tuple_begin(view.len);
|
||||
@@ -741,7 +201,7 @@ static Janet cfun_bytes(int32_t argc, Janet *argv) {
|
||||
return janet_wrap_tuple(janet_tuple_end(tup));
|
||||
}
|
||||
|
||||
static Janet cfun_frombytes(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_string_frombytes(int32_t argc, Janet *argv) {
|
||||
int32_t i;
|
||||
uint8_t *buf = janet_string_begin(argc);
|
||||
for (i = 0; i < argc; i++) {
|
||||
@@ -751,7 +211,7 @@ static Janet cfun_frombytes(int32_t argc, Janet *argv) {
|
||||
return janet_wrap_string(janet_string_end(buf));
|
||||
}
|
||||
|
||||
static Janet cfun_asciilower(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_string_asciilower(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetByteView view = janet_getbytes(argv, 0);
|
||||
uint8_t *buf = janet_string_begin(view.len);
|
||||
@@ -766,7 +226,7 @@ static Janet cfun_asciilower(int32_t argc, Janet *argv) {
|
||||
return janet_wrap_string(janet_string_end(buf));
|
||||
}
|
||||
|
||||
static Janet cfun_asciiupper(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_string_asciiupper(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetByteView view = janet_getbytes(argv, 0);
|
||||
uint8_t *buf = janet_string_begin(view.len);
|
||||
@@ -781,7 +241,7 @@ static Janet cfun_asciiupper(int32_t argc, Janet *argv) {
|
||||
return janet_wrap_string(janet_string_end(buf));
|
||||
}
|
||||
|
||||
static Janet cfun_reverse(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_string_reverse(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetByteView view = janet_getbytes(argv, 0);
|
||||
uint8_t *buf = janet_string_begin(view.len);
|
||||
@@ -805,7 +265,7 @@ static void findsetup(int32_t argc, Janet *argv, struct kmp_state *s, int32_t ex
|
||||
s->i = start;
|
||||
}
|
||||
|
||||
static Janet cfun_find(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_string_find(int32_t argc, Janet *argv) {
|
||||
int32_t result;
|
||||
struct kmp_state state;
|
||||
findsetup(argc, argv, &state, 0);
|
||||
@@ -816,7 +276,7 @@ static Janet cfun_find(int32_t argc, Janet *argv) {
|
||||
: janet_wrap_integer(result);
|
||||
}
|
||||
|
||||
static Janet cfun_findall(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_string_findall(int32_t argc, Janet *argv) {
|
||||
int32_t result;
|
||||
struct kmp_state state;
|
||||
findsetup(argc, argv, &state, 0);
|
||||
@@ -850,7 +310,7 @@ static void replacesetup(int32_t argc, Janet *argv, struct replace_state *s) {
|
||||
s->substlen = subst.len;
|
||||
}
|
||||
|
||||
static Janet cfun_replace(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_string_replace(int32_t argc, Janet *argv) {
|
||||
int32_t result;
|
||||
struct replace_state s;
|
||||
uint8_t *buf;
|
||||
@@ -870,7 +330,7 @@ static Janet cfun_replace(int32_t argc, Janet *argv) {
|
||||
return janet_wrap_string(janet_string_end(buf));
|
||||
}
|
||||
|
||||
static Janet cfun_replaceall(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_string_replaceall(int32_t argc, Janet *argv) {
|
||||
int32_t result;
|
||||
struct replace_state s;
|
||||
JanetBuffer b;
|
||||
@@ -890,7 +350,7 @@ static Janet cfun_replaceall(int32_t argc, Janet *argv) {
|
||||
return janet_wrap_string(ret);
|
||||
}
|
||||
|
||||
static Janet cfun_split(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_string_split(int32_t argc, Janet *argv) {
|
||||
int32_t result;
|
||||
JanetArray *array;
|
||||
struct kmp_state state;
|
||||
@@ -913,7 +373,7 @@ static Janet cfun_split(int32_t argc, Janet *argv) {
|
||||
return janet_wrap_array(array);
|
||||
}
|
||||
|
||||
static Janet cfun_checkset(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_string_checkset(int32_t argc, Janet *argv) {
|
||||
uint32_t bitset[8] = {0, 0, 0, 0, 0, 0, 0, 0};
|
||||
janet_arity(argc, 2, 3);
|
||||
JanetByteView set = janet_getbytes(argv, 0);
|
||||
@@ -941,7 +401,7 @@ static Janet cfun_checkset(int32_t argc, Janet *argv) {
|
||||
return janet_wrap_true();
|
||||
}
|
||||
|
||||
static Janet cfun_join(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_string_join(int32_t argc, Janet *argv) {
|
||||
janet_arity(argc, 1, 2);
|
||||
JanetView parts = janet_getindexed(argv, 0);
|
||||
JanetByteView joiner;
|
||||
@@ -994,7 +454,7 @@ static struct formatter {
|
||||
{"F", "%F", "%.*F"}
|
||||
};
|
||||
|
||||
static Janet cfun_number(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_string_number(int32_t argc, Janet *argv) {
|
||||
janet_arity(argc, 1, 4);
|
||||
double x = janet_getnumber(argv, 0);
|
||||
struct formatter fmter = formatters[0];
|
||||
@@ -1029,7 +489,7 @@ static Janet cfun_number(int32_t argc, Janet *argv) {
|
||||
return janet_cstringv(buf);
|
||||
}
|
||||
|
||||
static Janet cfun_pretty(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_string_pretty(int32_t argc, Janet *argv) {
|
||||
janet_arity(argc, 1, 3);
|
||||
JanetBuffer *buffer = NULL;
|
||||
int32_t depth = 4;
|
||||
@@ -1041,9 +501,9 @@ static Janet cfun_pretty(int32_t argc, Janet *argv) {
|
||||
return janet_wrap_buffer(buffer);
|
||||
}
|
||||
|
||||
static const JanetReg cfuns[] = {
|
||||
static const JanetReg string_cfuns[] = {
|
||||
{
|
||||
"string/slice", cfun_slice,
|
||||
"string/slice", cfun_string_slice,
|
||||
JDOC("(string/slice bytes [,start=0 [,end=(length str)]])\n\n"
|
||||
"Returns a substring from a byte sequence. The substring is from "
|
||||
"index start inclusive to index end exclusive. All indexing "
|
||||
@@ -1051,49 +511,49 @@ static const JanetReg cfuns[] = {
|
||||
"from the end of the string.")
|
||||
},
|
||||
{
|
||||
"string/repeat", cfun_repeat,
|
||||
"string/repeat", cfun_string_repeat,
|
||||
JDOC("(string/repeat bytes n)\n\n"
|
||||
"Returns a string that is n copies of bytes concatenated.")
|
||||
},
|
||||
{
|
||||
"string/bytes", cfun_bytes,
|
||||
"string/bytes", cfun_string_bytes,
|
||||
JDOC("(string/bytes str)\n\n"
|
||||
"Returns an array of integers that are the byte values of the string.")
|
||||
},
|
||||
{
|
||||
"string/from-bytes", cfun_frombytes,
|
||||
"string/from-bytes", cfun_string_frombytes,
|
||||
JDOC("(string/from-bytes byte-array)\n\n"
|
||||
"Creates a string from an array of integers with byte values. All integers "
|
||||
"will be coerced to the range of 1 byte 0-255.")
|
||||
},
|
||||
{
|
||||
"string/ascii-lower", cfun_asciilower,
|
||||
"string/ascii-lower", cfun_string_asciilower,
|
||||
JDOC("(string/ascii-lower str)\n\n"
|
||||
"Returns a new string where all bytes are replaced with the "
|
||||
"lowercase version of themselves in ASCII. Does only a very simple "
|
||||
"case check, meaning no unicode support.")
|
||||
},
|
||||
{
|
||||
"string/ascii-upper", cfun_asciiupper,
|
||||
"string/ascii-upper", cfun_string_asciiupper,
|
||||
JDOC("(string/ascii-upper str)\n\n"
|
||||
"Returns a new string where all bytes are replaced with the "
|
||||
"uppercase version of themselves in ASCII. Does only a very simple "
|
||||
"case check, meaning no unicode support.")
|
||||
},
|
||||
{
|
||||
"string/reverse", cfun_reverse,
|
||||
"string/reverse", cfun_string_reverse,
|
||||
JDOC("(string/reverse str)\n\n"
|
||||
"Returns a string that is the reversed version of str.")
|
||||
},
|
||||
{
|
||||
"string/find", cfun_find,
|
||||
"string/find", cfun_string_find,
|
||||
JDOC("(string/find patt str)\n\n"
|
||||
"Searches for the first instance of pattern patt in string "
|
||||
"str. Returns the index of the first character in patt if found, "
|
||||
"otherwise returns nil.")
|
||||
},
|
||||
{
|
||||
"string/find-all", cfun_findall,
|
||||
"string/find-all", cfun_string_findall,
|
||||
JDOC("(string/find patt str)\n\n"
|
||||
"Searches for all instances of pattern patt in string "
|
||||
"str. Returns an array of all indices of found patterns. Overlapping "
|
||||
@@ -1102,38 +562,38 @@ static const JanetReg cfuns[] = {
|
||||
"occurrences are found, will return an empty array.")
|
||||
},
|
||||
{
|
||||
"string/replace", cfun_replace,
|
||||
"string/replace", cfun_string_replace,
|
||||
JDOC("(string/replace patt subst str)\n\n"
|
||||
"Replace the first occurrence of patt with subst in the string str. "
|
||||
"Will return the new string if patt is found, otherwise returns str.")
|
||||
},
|
||||
{
|
||||
"string/replace-all", cfun_replaceall,
|
||||
"string/replace-all", cfun_string_replaceall,
|
||||
JDOC("(string/replace-all patt subst str)\n\n"
|
||||
"Replace all instances of patt with subst in the string str. "
|
||||
"Will return the new string if patt is found, otherwise returns str.")
|
||||
},
|
||||
{
|
||||
"string/split", cfun_split,
|
||||
"string/split", cfun_string_split,
|
||||
JDOC("(string/split delim str)\n\n"
|
||||
"Splits a string str with delimiter delim and returns an array of "
|
||||
"substrings. The substrings will not contain the delimiter delim. If delim "
|
||||
"is not found, the returned array will have one element.")
|
||||
},
|
||||
{
|
||||
"string/check-set", cfun_checkset,
|
||||
"string/check-set", cfun_string_checkset,
|
||||
JDOC("(string/check-set set str)\n\n"
|
||||
"Checks if any of the bytes in the string set appear in the string str. "
|
||||
"Returns true if some bytes in set do appear in str, false if no bytes do.")
|
||||
},
|
||||
{
|
||||
"string/join", cfun_join,
|
||||
"string/join", cfun_string_join,
|
||||
JDOC("(string/join parts [,sep])\n\n"
|
||||
"Joins an array of strings into one string, optionally separated by "
|
||||
"a separator string sep.")
|
||||
},
|
||||
{
|
||||
"string/number", cfun_number,
|
||||
"string/number", cfun_string_number,
|
||||
JDOC("(string/number x [,format [,maxlen [,precision]]])\n\n"
|
||||
"Formats a number as string. The format parameter indicates how "
|
||||
"to display the number, either as floating point, scientific, or "
|
||||
@@ -1149,7 +609,7 @@ static const JanetReg cfuns[] = {
|
||||
"Returns a string representation of x.")
|
||||
},
|
||||
{
|
||||
"string/pretty", cfun_pretty,
|
||||
"string/pretty", cfun_string_pretty,
|
||||
JDOC("(string/pretty x [,depth=4 [,buffer=@\"\"]])\n\n"
|
||||
"Pretty prints a value to a buffer. Optionally allows setting max "
|
||||
"recursion depth, as well as writing to a buffer. Returns the buffer.")
|
||||
@@ -1159,5 +619,5 @@ static const JanetReg cfuns[] = {
|
||||
|
||||
/* Module entry point */
|
||||
void janet_lib_string(JanetTable *env) {
|
||||
janet_cfuns(env, NULL, cfuns);
|
||||
janet_core_cfuns(env, NULL, string_cfuns);
|
||||
}
|
||||
|
||||
@@ -44,10 +44,13 @@
|
||||
* as it will not fit in the range for a signed 32 bit integer. The string
|
||||
* '0xbeef' would parse to an integer as it is in the range of an int32_t. */
|
||||
|
||||
#include <janet/janet.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet/janet.h>
|
||||
#endif
|
||||
|
||||
/* Lookup table for getting values of characters when parsing numbers. Handles
|
||||
* digits 0-9 and a-z (and A-Z). A-Z have values of 10 to 35. */
|
||||
static uint8_t digit_lookup[128] = {
|
||||
@@ -225,7 +228,7 @@ static double convert(
|
||||
bignat_lshift_n(mant, shamt);
|
||||
exponent2 -= shamt * BIGNAT_NBIT;
|
||||
for (;exponent < -3; exponent += 4) bignat_div(mant, base * base * base * base);
|
||||
for (;exponent < -2; exponent += 2) bignat_div(mant, base * base);
|
||||
for (;exponent < -1; exponent += 2) bignat_div(mant, base * base);
|
||||
for (;exponent < 0; exponent += 1) bignat_div(mant, base);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,9 +20,12 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet/janet.h>
|
||||
#include "gc.h"
|
||||
#include "util.h"
|
||||
#include <math.h>
|
||||
#endif
|
||||
|
||||
/* Begin creation of a struct */
|
||||
JanetKV *janet_struct_begin(int32_t count) {
|
||||
@@ -71,6 +74,7 @@ void janet_struct_put(JanetKV *st, Janet key, Janet value) {
|
||||
int32_t i, j, dist;
|
||||
int32_t bounds[4] = {index, cap, 0, index};
|
||||
if (janet_checktype(key, JANET_NIL) || janet_checktype(value, JANET_NIL)) return;
|
||||
if (janet_checktype(key, JANET_NUMBER) && isnan(janet_unwrap_number(key))) return;
|
||||
/* Avoid extra items */
|
||||
if (janet_struct_hash(st) == janet_struct_length(st)) return;
|
||||
for (dist = 0, j = 0; j < 4; j += 2)
|
||||
@@ -118,9 +122,7 @@ void janet_struct_put(JanetKV *st, Janet key, Janet value) {
|
||||
dist = otherdist;
|
||||
hash = otherhash;
|
||||
} else if (status == 0) {
|
||||
/* This should not happen - it means
|
||||
* than a key was added to the struct more than once */
|
||||
janet_exit("struct double put fail");
|
||||
/* A key was added to the struct more than once */
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -132,15 +134,8 @@ const JanetKV *janet_struct_end(JanetKV *st) {
|
||||
/* Error building struct, probably duplicate values. We need to rebuild
|
||||
* the struct using only the values that went in. The second creation should always
|
||||
* succeed. */
|
||||
int32_t i, realCount;
|
||||
JanetKV *newst;
|
||||
realCount = 0;
|
||||
for (i = 0; i < janet_struct_capacity(st); i++) {
|
||||
JanetKV *kv = st + i;
|
||||
realCount += janet_checktype(kv->key, JANET_NIL) ? 1 : 0;
|
||||
}
|
||||
newst = janet_struct_begin(realCount);
|
||||
for (i = 0; i < janet_struct_capacity(st); i++) {
|
||||
JanetKV *newst = janet_struct_begin(janet_struct_hash(st));
|
||||
for (int32_t i = 0; i < janet_struct_capacity(st); i++) {
|
||||
JanetKV *kv = st + i;
|
||||
if (!janet_checktype(kv->key, JANET_NIL)) {
|
||||
janet_struct_put(newst, kv->key, kv->value);
|
||||
@@ -218,5 +213,3 @@ int janet_struct_compare(const JanetKV *lhs, const JanetKV *rhs) {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#undef janet_maphash
|
||||
|
||||
@@ -25,10 +25,12 @@
|
||||
* checks, all symbols are interned so that there is a single copy of it in the
|
||||
* whole program. Equality is then just a pointer check. */
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet/janet.h>
|
||||
#include "state.h"
|
||||
#include "gc.h"
|
||||
#include "util.h"
|
||||
#endif
|
||||
|
||||
/* Cache state */
|
||||
JANET_THREAD_LOCAL const uint8_t **janet_vm_cache = NULL;
|
||||
|
||||
@@ -23,7 +23,9 @@
|
||||
#ifndef JANET_SYMCACHE_H_defined
|
||||
#define JANET_SYMCACHE_H_defined
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet/janet.h>
|
||||
#endif
|
||||
|
||||
/* Initialize the cache (allocate cache memory) */
|
||||
void janet_symcache_init(void);
|
||||
|
||||
@@ -20,9 +20,12 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet/janet.h>
|
||||
#include "gc.h"
|
||||
#include "util.h"
|
||||
#include <math.h>
|
||||
#endif
|
||||
|
||||
/* Initialize a table */
|
||||
JanetTable *janet_table_init(JanetTable *table, int32_t capacity) {
|
||||
@@ -129,6 +132,7 @@ Janet janet_table_remove(JanetTable *t, Janet key) {
|
||||
/* Put a value into the object */
|
||||
void janet_table_put(JanetTable *t, Janet key, Janet value) {
|
||||
if (janet_checktype(key, JANET_NIL)) return;
|
||||
if (janet_checktype(key, JANET_NUMBER) && isnan(janet_unwrap_number(key))) return;
|
||||
if (janet_checktype(value, JANET_NIL)) {
|
||||
janet_table_remove(t, key);
|
||||
} else {
|
||||
@@ -194,13 +198,13 @@ void janet_table_merge_struct(JanetTable *table, const JanetKV *other) {
|
||||
|
||||
/* C Functions */
|
||||
|
||||
static Janet cfun_new(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_table_new(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
int32_t cap = janet_getinteger(argv, 0);
|
||||
return janet_wrap_table(janet_table(cap));
|
||||
}
|
||||
|
||||
static Janet cfun_getproto(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_table_getproto(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetTable *t = janet_gettable(argv, 0);
|
||||
return t->proto
|
||||
@@ -208,7 +212,7 @@ static Janet cfun_getproto(int32_t argc, Janet *argv) {
|
||||
: janet_wrap_nil();
|
||||
}
|
||||
|
||||
static Janet cfun_setproto(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_table_setproto(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 2);
|
||||
JanetTable *table = janet_gettable(argv, 0);
|
||||
JanetTable *proto = NULL;
|
||||
@@ -219,21 +223,21 @@ static Janet cfun_setproto(int32_t argc, Janet *argv) {
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static Janet cfun_tostruct(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_table_tostruct(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetTable *t = janet_gettable(argv, 0);
|
||||
return janet_wrap_struct(janet_table_to_struct(t));
|
||||
}
|
||||
|
||||
static Janet cfun_rawget(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_table_rawget(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 2);
|
||||
JanetTable *table = janet_gettable(argv, 0);
|
||||
return janet_table_rawget(table, argv[1]);
|
||||
}
|
||||
|
||||
static const JanetReg cfuns[] = {
|
||||
static const JanetReg table_cfuns[] = {
|
||||
{
|
||||
"table/new", cfun_new,
|
||||
"table/new", cfun_table_new,
|
||||
JDOC("(table/new capacity)\n\n"
|
||||
"Creates a new empty table with pre-allocated memory "
|
||||
"for capacity entries. This means that if one knows the number of "
|
||||
@@ -241,24 +245,24 @@ static const JanetReg cfuns[] = {
|
||||
"can be avoided. Returns the new table.")
|
||||
},
|
||||
{
|
||||
"table/to-struct", cfun_tostruct,
|
||||
"table/to-struct", cfun_table_tostruct,
|
||||
JDOC("(table/to-struct tab)\n\n"
|
||||
"Convert a table to a struct. Returns a new struct. This function "
|
||||
"does not take into account prototype tables.")
|
||||
},
|
||||
{
|
||||
"table/getproto", cfun_getproto,
|
||||
"table/getproto", cfun_table_getproto,
|
||||
JDOC("(table/getproto tab)\n\n"
|
||||
"Get the prototype table of a table. Returns nil if a table "
|
||||
"has no prototype, otherwise returns the prototype.")
|
||||
},
|
||||
{
|
||||
"table/setproto", cfun_setproto,
|
||||
"table/setproto", cfun_table_setproto,
|
||||
JDOC("(table/setproto tab proto)\n\n"
|
||||
"Set the prototype of a table. Returns the original table tab.")
|
||||
},
|
||||
{
|
||||
"table/rawget", cfun_rawget,
|
||||
"table/rawget", cfun_table_rawget,
|
||||
JDOC("(table/rawget tab key)\n\n"
|
||||
"Gets a value from a table without looking at the prototype table. "
|
||||
"If a table tab does not contain t directly, the function will return "
|
||||
@@ -269,5 +273,5 @@ static const JanetReg cfuns[] = {
|
||||
|
||||
/* Load the table module */
|
||||
void janet_lib_table(JanetTable *env) {
|
||||
janet_cfuns(env, NULL, cfuns);
|
||||
janet_core_cfuns(env, NULL, table_cfuns);
|
||||
}
|
||||
|
||||
@@ -20,10 +20,12 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet/janet.h>
|
||||
#include "symcache.h"
|
||||
#include "gc.h"
|
||||
#include "util.h"
|
||||
#endif
|
||||
|
||||
/* Create a new empty tuple of the given size. This will return memory
|
||||
* which should be filled with Janets. The memory will not be collected until
|
||||
@@ -91,13 +93,13 @@ int janet_tuple_compare(const Janet *lhs, const Janet *rhs) {
|
||||
|
||||
/* C Functions */
|
||||
|
||||
static Janet cfun_slice(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_tuple_slice(int32_t argc, Janet *argv) {
|
||||
JanetRange range = janet_getslice(argc, argv);
|
||||
JanetView view = janet_getindexed(argv, 0);
|
||||
return janet_wrap_tuple(janet_tuple_n(view.items + range.start, range.end - range.start));
|
||||
}
|
||||
|
||||
static Janet cfun_prepend(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_tuple_prepend(int32_t argc, Janet *argv) {
|
||||
janet_arity(argc, 1, -1);
|
||||
JanetView view = janet_getindexed(argv, 0);
|
||||
Janet *n = janet_tuple_begin(view.len - 1 + argc);
|
||||
@@ -108,7 +110,7 @@ static Janet cfun_prepend(int32_t argc, Janet *argv) {
|
||||
return janet_wrap_tuple(janet_tuple_end(n));
|
||||
}
|
||||
|
||||
static Janet cfun_append(int32_t argc, Janet *argv) {
|
||||
static Janet cfun_tuple_append(int32_t argc, Janet *argv) {
|
||||
janet_arity(argc, 1, -1);
|
||||
JanetView view = janet_getindexed(argv, 0);
|
||||
Janet *n = janet_tuple_begin(view.len - 1 + argc);
|
||||
@@ -117,9 +119,9 @@ static Janet cfun_append(int32_t argc, Janet *argv) {
|
||||
return janet_wrap_tuple(janet_tuple_end(n));
|
||||
}
|
||||
|
||||
static const JanetReg cfuns[] = {
|
||||
static const JanetReg tuple_cfuns[] = {
|
||||
{
|
||||
"tuple/slice", cfun_slice,
|
||||
"tuple/slice", cfun_tuple_slice,
|
||||
JDOC("(tuple/slice arrtup [,start=0 [,end=(length arrtup)]])\n\n"
|
||||
"Take a sub sequence of an array or tuple from index start "
|
||||
"inclusive to index end exclusive. If start or end are not provided, "
|
||||
@@ -127,13 +129,13 @@ static const JanetReg cfuns[] = {
|
||||
"Returns the new tuple.")
|
||||
},
|
||||
{
|
||||
"tuple/append", cfun_append,
|
||||
"tuple/append", cfun_tuple_append,
|
||||
JDOC("(tuple/append tup & items)\n\n"
|
||||
"Returns a new tuple that is the result of appending "
|
||||
"each element in items to tup.")
|
||||
},
|
||||
{
|
||||
"tuple/prepend", cfun_prepend,
|
||||
"tuple/prepend", cfun_tuple_prepend,
|
||||
JDOC("(tuple/prepend tup & items)\n\n"
|
||||
"Prepends each element in items to tuple and "
|
||||
"returns a new tuple. Items are prepended such that the "
|
||||
@@ -144,5 +146,5 @@ static const JanetReg cfuns[] = {
|
||||
|
||||
/* Load the tuple module */
|
||||
void janet_lib_tuple(JanetTable *env) {
|
||||
janet_cfuns(env, NULL, cfuns);
|
||||
janet_core_cfuns(env, NULL, tuple_cfuns);
|
||||
}
|
||||
|
||||
@@ -20,11 +20,14 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <janet/janet.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet/janet.h>
|
||||
#include "util.h"
|
||||
#include "state.h"
|
||||
#include "gc.h"
|
||||
#endif
|
||||
|
||||
/* Base 64 lookup table for digits */
|
||||
const char janet_base64[65] =
|
||||
@@ -281,6 +284,24 @@ void janet_cfuns(JanetTable *env, const char *regprefix, const JanetReg *cfuns)
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef JANET_BOOTSTRAP
|
||||
void janet_core_def(JanetTable *env, const char *name, Janet x, const void *p) {
|
||||
(void) p;
|
||||
janet_table_put(env, janet_csymbolv(name), x);
|
||||
}
|
||||
|
||||
void janet_core_cfuns(JanetTable *env, const char *regprefix, const JanetReg *cfuns) {
|
||||
(void) regprefix;
|
||||
while (cfuns->name) {
|
||||
Janet name = janet_csymbolv(cfuns->name);
|
||||
Janet fun = janet_wrap_cfunction(cfuns->cfun);
|
||||
janet_core_def(env, cfuns->name, fun, cfuns->documentation);
|
||||
janet_table_put(janet_vm_registry, fun, name);
|
||||
cfuns++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Resolve a symbol in the environment */
|
||||
JanetBindingType janet_resolve(JanetTable *env, const uint8_t *sym, Janet *out) {
|
||||
Janet ref;
|
||||
|
||||
@@ -23,11 +23,14 @@
|
||||
#ifndef JANET_UTIL_H_defined
|
||||
#define JANET_UTIL_H_defined
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet/janet.h>
|
||||
#endif
|
||||
|
||||
/* Omit docstrings in some builds */
|
||||
#ifdef JANET_NO_BOOTSTRAP
|
||||
#ifndef JANET_BOOTSTRAP
|
||||
#define JDOC(x) NULL
|
||||
#define JANET_NO_BOOTSTRAP
|
||||
#else
|
||||
#define JDOC(x) x
|
||||
#endif
|
||||
@@ -50,6 +53,16 @@ const void *janet_strbinsearch(
|
||||
size_t itemsize,
|
||||
const uint8_t *key);
|
||||
|
||||
/* Inside the janet core, defining globals is different
|
||||
* at bootstrap time and normal runtime */
|
||||
#ifdef JANET_BOOTSTRAP
|
||||
#define janet_core_def janet_def
|
||||
#define janet_core_cfuns janet_cfuns
|
||||
#else
|
||||
void janet_core_def(JanetTable *env, const char *name, Janet x, const void *p);
|
||||
void janet_core_cfuns(JanetTable *env, const char *regprefix, const JanetReg *cfuns);
|
||||
#endif
|
||||
|
||||
/* Initialize builtin libraries */
|
||||
void janet_lib_io(JanetTable *env);
|
||||
void janet_lib_math(JanetTable *env);
|
||||
@@ -67,5 +80,6 @@ void janet_lib_asm(JanetTable *env);
|
||||
#endif
|
||||
void janet_lib_compile(JanetTable *env);
|
||||
void janet_lib_debug(JanetTable *env);
|
||||
void janet_lib_peg(JanetTable *env);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -20,7 +20,9 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet/janet.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Define a number of functions that can be used internally on ANY Janet.
|
||||
@@ -217,6 +219,17 @@ Janet janet_get(Janet ds, Janet key) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case JANET_ABSTRACT:
|
||||
{
|
||||
JanetAbstractType *type = (JanetAbstractType *)janet_abstract_type(janet_unwrap_abstract(ds));
|
||||
if (type->get) {
|
||||
value = (type->get)(janet_unwrap_abstract(ds),key);
|
||||
} else {
|
||||
janet_panicf("no getter for %T ", JANET_TFLAG_LENGTHABLE, ds);
|
||||
value = janet_wrap_nil();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
@@ -265,6 +278,17 @@ Janet janet_getindex(Janet ds, int32_t index) {
|
||||
case JANET_STRUCT:
|
||||
value = janet_struct_get(janet_unwrap_struct(ds), janet_wrap_integer(index));
|
||||
break;
|
||||
case JANET_ABSTRACT:
|
||||
{
|
||||
JanetAbstractType *type = (JanetAbstractType *)janet_abstract_type(janet_unwrap_abstract(ds));
|
||||
if (type->get) {
|
||||
value = (type->get)(janet_unwrap_abstract(ds),janet_wrap_integer(index));
|
||||
} else {
|
||||
janet_panicf("no getter for %T ", JANET_TFLAG_LENGTHABLE, ds);
|
||||
value = janet_wrap_nil();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
@@ -325,6 +349,16 @@ void janet_putindex(Janet ds, int32_t index, Janet value) {
|
||||
janet_table_put(table, janet_wrap_integer(index), value);
|
||||
break;
|
||||
}
|
||||
case JANET_ABSTRACT:
|
||||
{
|
||||
JanetAbstractType *type = (JanetAbstractType *)janet_abstract_type(janet_unwrap_abstract(ds));
|
||||
if (type->put) {
|
||||
(type->put)(janet_unwrap_abstract(ds),janet_wrap_integer(index),value);
|
||||
} else {
|
||||
janet_panicf("no setter for %T ", JANET_TFLAG_LENGTHABLE, ds);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -365,5 +399,16 @@ void janet_put(Janet ds, Janet key, Janet value) {
|
||||
case JANET_TABLE:
|
||||
janet_table_put(janet_unwrap_table(ds), key, value);
|
||||
break;
|
||||
case JANET_ABSTRACT:
|
||||
{
|
||||
JanetAbstractType *type = (JanetAbstractType *)janet_abstract_type(janet_unwrap_abstract(ds));
|
||||
if (type->put) {
|
||||
(type->put)(janet_unwrap_abstract(ds),key,value);
|
||||
} else {
|
||||
janet_panicf("no setter for %T ", JANET_TFLAG_LENGTHABLE, ds);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,9 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include "vector.h"
|
||||
#endif
|
||||
|
||||
/* Grow the buffer dynamically. Used for push operations. */
|
||||
void *janet_v_grow(void *v, int32_t increment, int32_t itemsize) {
|
||||
@@ -40,22 +42,6 @@ void *janet_v_grow(void *v, int32_t increment, int32_t itemsize) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Clone a buffer. */
|
||||
void *janet_v_copymem(void *v, int32_t itemsize) {
|
||||
int32_t *p;
|
||||
if (NULL == v) return NULL;
|
||||
p = malloc(2 * sizeof(int32_t) + itemsize * janet_v__cap(v));
|
||||
if (NULL != p) {
|
||||
memcpy(p, janet_v__raw(v), 2 * sizeof(int32_t) + itemsize * janet_v__cnt(v));
|
||||
return p + 2;
|
||||
} else {
|
||||
{
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
return (void *) (2 * sizeof(int32_t));
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert a buffer to normal allocated memory (forget capacity) */
|
||||
void *janet_v_flattenmem(void *v, int32_t itemsize) {
|
||||
int32_t *p;
|
||||
|
||||
@@ -23,7 +23,9 @@
|
||||
#ifndef JANET_VECTOR_H_defined
|
||||
#define JANET_VECTOR_H_defined
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet/janet.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* vector code modified from
|
||||
@@ -38,7 +40,6 @@
|
||||
#define janet_v_push(v, x) (janet_v__maybegrow(v, 1), (v)[janet_v__cnt(v)++] = (x))
|
||||
#define janet_v_pop(v) (janet_v_count(v) ? janet_v__cnt(v)-- : 0)
|
||||
#define janet_v_count(v) (((v) != NULL) ? janet_v__cnt(v) : 0)
|
||||
#define janet_v_add(v, n) (janet_v__maybegrow(v, n), janet_v_cnt(v) += (n), &(v)[janet_v__cnt(v) - (n)])
|
||||
#define janet_v_last(v) ((v)[janet_v__cnt(v) - 1])
|
||||
#define janet_v_empty(v) (((v) != NULL) ? (janet_v__cnt(v) = 0) : 0)
|
||||
#define janet_v_copy(v) (janet_v_copymem((v), sizeof(*(v))))
|
||||
|
||||
108
src/core/vm.c
108
src/core/vm.c
@@ -20,17 +20,21 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet/janet.h>
|
||||
#include "state.h"
|
||||
#include "fiber.h"
|
||||
#include "gc.h"
|
||||
#include "symcache.h"
|
||||
#include "util.h"
|
||||
#endif
|
||||
|
||||
/* VM state */
|
||||
JANET_THREAD_LOCAL JanetTable *janet_vm_registry;
|
||||
JANET_THREAD_LOCAL int janet_vm_stackn = 0;
|
||||
JANET_THREAD_LOCAL JanetFiber *janet_vm_fiber = NULL;
|
||||
JANET_THREAD_LOCAL Janet *janet_vm_return_reg = NULL;
|
||||
JANET_THREAD_LOCAL jmp_buf *janet_vm_jmp_buf = NULL;
|
||||
|
||||
/* Virtual registers
|
||||
*
|
||||
@@ -58,7 +62,7 @@ JANET_THREAD_LOCAL JanetFiber *janet_vm_fiber = NULL;
|
||||
#define VM_END() }
|
||||
#define VM_OP(op) label_##op :
|
||||
#define VM_DEFAULT() label_unknown_op:
|
||||
#define vm_next() goto *op_lookup[*pc & 0xFF];
|
||||
#define vm_next() goto *op_lookup[*pc & 0xFF]
|
||||
static void *op_lookup[255] = {
|
||||
&&label_JOP_NOOP,
|
||||
&&label_JOP_ERROR,
|
||||
@@ -146,7 +150,7 @@ static void *op_lookup[255] = {
|
||||
} while (0)
|
||||
#define vm_return(sig, val) do { \
|
||||
vm_commit(); \
|
||||
janet_fiber_push(fiber, (val)); \
|
||||
janet_vm_return_reg[0] = (val); \
|
||||
return (sig); \
|
||||
} while (0)
|
||||
|
||||
@@ -224,7 +228,7 @@ static Janet call_nonfn(JanetFiber *fiber, Janet callee) {
|
||||
int32_t argn = fiber->stacktop - fiber->stackstart;
|
||||
Janet ds, key;
|
||||
if (argn != 1) janet_panicf("%v called with arity %d, expected 1", callee, argn);
|
||||
if (janet_checktypes(callee, JANET_TFLAG_INDEXED | JANET_TFLAG_DICTIONARY)) {
|
||||
if (janet_checktypes(callee, JANET_TFLAG_INDEXED | JANET_TFLAG_DICTIONARY | JANET_TFLAG_ABSTRACT)) {
|
||||
ds = callee;
|
||||
key = fiber->data[fiber->stackstart];
|
||||
} else {
|
||||
@@ -236,7 +240,7 @@ static Janet call_nonfn(JanetFiber *fiber, Janet callee) {
|
||||
}
|
||||
|
||||
/* Interpreter main loop */
|
||||
static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
||||
static JanetSignal run_vm(JanetFiber *fiber, Janet in, JanetFiberStatus status) {
|
||||
|
||||
/* Interpreter state */
|
||||
register Janet *stack;
|
||||
@@ -248,7 +252,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
||||
* waiting to be resumed. In those cases, use input and increment pc. We
|
||||
* DO NOT use input when resuming a fiber that has been interrupted at a
|
||||
* breakpoint. */
|
||||
if (janet_fiber_status(fiber) != JANET_STATUS_NEW &&
|
||||
if (status != JANET_STATUS_NEW &&
|
||||
((*pc & 0xFF) == JOP_SIGNAL || (*pc & 0xFF) == JOP_RESUME)) {
|
||||
stack[A] = in;
|
||||
pc++;
|
||||
@@ -257,7 +261,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
||||
/* The first opcode to execute. If the first opcode has
|
||||
* the breakpoint bit set and we were in the debug state, skip
|
||||
* that first breakpoint. */
|
||||
uint8_t first_opcode = (janet_fiber_status(fiber) == JANET_STATUS_DEBUG)
|
||||
uint8_t first_opcode = (status == JANET_STATUS_DEBUG)
|
||||
? (*pc & 0x7F)
|
||||
: (*pc & 0xFF);
|
||||
|
||||
@@ -281,8 +285,9 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
||||
VM_OP(JOP_RETURN)
|
||||
{
|
||||
Janet retval = stack[D];
|
||||
int entrance_frame = janet_stack_frame(stack)->flags & JANET_STACKFRAME_ENTRANCE;
|
||||
janet_fiber_popframe(fiber);
|
||||
if (fiber->frame == 0) vm_return(JANET_SIGNAL_OK, retval);
|
||||
if (entrance_frame) vm_return(JANET_SIGNAL_OK, retval);
|
||||
vm_restore();
|
||||
stack[A] = retval;
|
||||
vm_checkgc_pcnext();
|
||||
@@ -291,8 +296,9 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
||||
VM_OP(JOP_RETURN_NIL)
|
||||
{
|
||||
Janet retval = janet_wrap_nil();
|
||||
int entrance_frame = janet_stack_frame(stack)->flags & JANET_STACKFRAME_ENTRANCE;
|
||||
janet_fiber_popframe(fiber);
|
||||
if (fiber->frame == 0) vm_return(JANET_SIGNAL_OK, retval);
|
||||
if (entrance_frame) vm_return(JANET_SIGNAL_OK, retval);
|
||||
vm_restore();
|
||||
stack[A] = retval;
|
||||
vm_checkgc_pcnext();
|
||||
@@ -579,7 +585,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
||||
janet_fiber_cframe(fiber, janet_unwrap_cfunction(callee));
|
||||
Janet ret = janet_unwrap_cfunction(callee)(argc, fiber->data + fiber->frame);
|
||||
janet_fiber_popframe(fiber);
|
||||
if (fiber->frame == 0) vm_return(JANET_SIGNAL_OK, ret);
|
||||
/*if (fiber->frame == 0) vm_return(JANET_SIGNAL_OK, ret);*/
|
||||
stack = fiber->data + fiber->frame;
|
||||
stack[A] = ret;
|
||||
vm_checkgc_pcnext();
|
||||
@@ -612,6 +618,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
||||
vm_checkgc_next();
|
||||
} else {
|
||||
Janet retreg;
|
||||
int entrance_frame = janet_stack_frame(stack)->flags & JANET_STACKFRAME_ENTRANCE;
|
||||
vm_commit();
|
||||
if (janet_checktype(callee, JANET_CFUNCTION)) {
|
||||
int32_t argc = fiber->stacktop - fiber->stackstart;
|
||||
@@ -622,7 +629,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
||||
retreg = call_nonfn(fiber, callee);
|
||||
}
|
||||
janet_fiber_popframe(fiber);
|
||||
if (fiber->frame == 0)
|
||||
if (entrance_frame)
|
||||
vm_return(JANET_SIGNAL_OK, retreg);
|
||||
vm_restore();
|
||||
stack[A] = retreg;
|
||||
@@ -752,19 +759,57 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
||||
VM_END()
|
||||
}
|
||||
|
||||
Janet janet_call(JanetFunction *fun, int32_t argc, const Janet *argv) {
|
||||
Janet ret;
|
||||
Janet *old_return_reg = janet_vm_return_reg;
|
||||
|
||||
/* Check entry conditions */
|
||||
if (!janet_vm_fiber)
|
||||
janet_panic("janet_call failed because there is no current fiber");
|
||||
if (janet_vm_stackn >= JANET_RECURSION_GUARD)
|
||||
janet_panic("C stack recursed too deeply");
|
||||
|
||||
/* Push frame */
|
||||
janet_fiber_pushn(janet_vm_fiber, argv, argc);
|
||||
if (janet_fiber_funcframe(janet_vm_fiber, fun)) {
|
||||
janet_panicf("arity mismatch in %v", fun);
|
||||
}
|
||||
janet_fiber_frame(janet_vm_fiber)->flags |= JANET_STACKFRAME_ENTRANCE;
|
||||
|
||||
/* Set up */
|
||||
int32_t oldn = janet_vm_stackn++;
|
||||
int handle = janet_gclock();
|
||||
janet_vm_return_reg = &ret;
|
||||
|
||||
/* Run vm */
|
||||
JanetSignal signal = run_vm(janet_vm_fiber,
|
||||
janet_wrap_nil(),
|
||||
JANET_STATUS_ALIVE);
|
||||
|
||||
/* Teardown */
|
||||
janet_vm_return_reg = old_return_reg;
|
||||
janet_vm_stackn = oldn;
|
||||
janet_gcunlock(handle);
|
||||
|
||||
if (signal != JANET_SIGNAL_OK) janet_panicv(ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Enter the main vm loop */
|
||||
JanetSignal janet_continue(JanetFiber *fiber, Janet in, Janet *out) {
|
||||
jmp_buf buf;
|
||||
|
||||
/* Check conditions */
|
||||
JanetFiberStatus old_status = janet_fiber_status(fiber);
|
||||
if (janet_vm_stackn >= JANET_RECURSION_GUARD) {
|
||||
janet_fiber_set_status(fiber, JANET_STATUS_ERROR);
|
||||
*out = janet_cstringv("C stack recursed too deeply");
|
||||
return JANET_SIGNAL_ERROR;
|
||||
}
|
||||
JanetFiberStatus startstatus = janet_fiber_status(fiber);
|
||||
if (startstatus == JANET_STATUS_ALIVE ||
|
||||
startstatus == JANET_STATUS_DEAD ||
|
||||
startstatus == JANET_STATUS_ERROR) {
|
||||
if (old_status == JANET_STATUS_ALIVE ||
|
||||
old_status == JANET_STATUS_DEAD ||
|
||||
old_status == JANET_STATUS_ERROR) {
|
||||
*out = janet_cstringv("cannot resume alive, dead, or errored fiber");
|
||||
return JANET_SIGNAL_ERROR;
|
||||
}
|
||||
@@ -782,40 +827,49 @@ JanetSignal janet_continue(JanetFiber *fiber, Janet in, Janet *out) {
|
||||
fiber->child = NULL;
|
||||
}
|
||||
|
||||
/* Prepare state */
|
||||
janet_vm_stackn++;
|
||||
janet_gcroot(janet_wrap_fiber(fiber));
|
||||
/* Save global state */
|
||||
int32_t oldn = janet_vm_stackn++;
|
||||
int handle = janet_vm_gc_suspend;
|
||||
JanetFiber *old_vm_fiber = janet_vm_fiber;
|
||||
jmp_buf *old_vm_jmp_buf = janet_vm_jmp_buf;
|
||||
Janet *old_vm_return_reg = janet_vm_return_reg;
|
||||
|
||||
/* Setup fiber */
|
||||
janet_vm_fiber = fiber;
|
||||
janet_gcroot(janet_wrap_fiber(fiber));
|
||||
janet_fiber_set_status(fiber, JANET_STATUS_ALIVE);
|
||||
janet_vm_return_reg = out;
|
||||
janet_vm_jmp_buf = &buf;
|
||||
|
||||
/* Run loop */
|
||||
JanetSignal signal;
|
||||
if (setjmp(fiber->buf)) {
|
||||
if (setjmp(buf)) {
|
||||
signal = JANET_SIGNAL_ERROR;
|
||||
} else {
|
||||
signal = run_vm(fiber, in);
|
||||
signal = run_vm(fiber, in, old_status);
|
||||
}
|
||||
|
||||
/* Tear down */
|
||||
/* Tear down fiber */
|
||||
janet_fiber_set_status(fiber, signal);
|
||||
janet_vm_fiber = old_vm_fiber;
|
||||
janet_vm_stackn--;
|
||||
janet_gcunroot(janet_wrap_fiber(fiber));
|
||||
|
||||
/* Pop error or return value from fiber stack */
|
||||
*out = fiber->data[--fiber->stacktop];
|
||||
/* Restore global state */
|
||||
janet_vm_gc_suspend = handle;
|
||||
janet_vm_fiber = old_vm_fiber;
|
||||
janet_vm_stackn = oldn;
|
||||
janet_vm_return_reg = old_vm_return_reg;
|
||||
janet_vm_jmp_buf = old_vm_jmp_buf;
|
||||
|
||||
return signal;
|
||||
}
|
||||
|
||||
JanetSignal janet_call(
|
||||
JanetSignal janet_pcall(
|
||||
JanetFunction *fun,
|
||||
int32_t argn,
|
||||
int32_t argc,
|
||||
const Janet *argv,
|
||||
Janet *out,
|
||||
JanetFiber **f) {
|
||||
JanetFiber *fiber = janet_fiber_n(fun, 64, argv, argn);
|
||||
JanetFiber *fiber = janet_fiber(fun, 64, argc, argv);
|
||||
if (f) *f = fiber;
|
||||
if (!fiber) {
|
||||
*out = janet_cstringv("arity mismatch");
|
||||
|
||||
@@ -20,7 +20,9 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet/janet.h>
|
||||
#endif
|
||||
|
||||
void *janet_memalloc_empty(int32_t count) {
|
||||
int32_t i;
|
||||
|
||||
@@ -29,7 +29,7 @@ extern "C" {
|
||||
|
||||
/***** START SECTION CONFIG *****/
|
||||
|
||||
#define JANET_VERSION "0.3.0"
|
||||
#define JANET_VERSION "0.4.0"
|
||||
|
||||
#ifndef JANET_BUILD
|
||||
#define JANET_BUILD "local"
|
||||
@@ -154,9 +154,6 @@ extern "C" {
|
||||
#define JANET_OUT_OF_MEMORY do { printf("janet out of memory\n"); exit(1); } while (0)
|
||||
#endif
|
||||
|
||||
/* Helper for debugging */
|
||||
#define janet_trace(x) janet_puts(janet_formatc("JANET TRACE %s, %d: %v\n", __FILE__, __LINE__, x))
|
||||
|
||||
/* Prevent some recursive functions from recursing too deeply
|
||||
* ands crashing (the parser). Instead, error out. */
|
||||
#define JANET_RECURSION_GUARD 1024
|
||||
@@ -269,6 +266,7 @@ typedef struct JanetKV JanetKV;
|
||||
typedef struct JanetStackFrame JanetStackFrame;
|
||||
typedef struct JanetAbstractType JanetAbstractType;
|
||||
typedef struct JanetReg JanetReg;
|
||||
typedef struct JanetMethod JanetMethod;
|
||||
typedef struct JanetSourceMapping JanetSourceMapping;
|
||||
typedef struct JanetView JanetView;
|
||||
typedef struct JanetByteView JanetByteView;
|
||||
@@ -606,12 +604,14 @@ struct JanetFiber {
|
||||
int32_t capacity;
|
||||
int32_t maxstack; /* Arbitrary defined limit for stack overflow */
|
||||
int32_t flags; /* Various flags */
|
||||
jmp_buf buf; /* Handle errors */
|
||||
};
|
||||
|
||||
/* Mark if a stack frame is a tail call for debugging */
|
||||
#define JANET_STACKFRAME_TAILCALL 1
|
||||
|
||||
/* Mark if a stack frame is an entrance frame */
|
||||
#define JANET_STACKFRAME_ENTRANCE 2
|
||||
|
||||
/* A stack frame on the fiber. Is stored along with the stack values. */
|
||||
struct JanetStackFrame {
|
||||
JanetFunction *func;
|
||||
@@ -739,6 +739,8 @@ struct JanetAbstractType {
|
||||
const char *name;
|
||||
int (*gc)(void *data, size_t len);
|
||||
int (*gcmark)(void *data, size_t len);
|
||||
Janet (*get)(void *data, Janet key);
|
||||
void (*put)(void *data, Janet key, Janet value);
|
||||
};
|
||||
|
||||
/* Contains information about abstract types */
|
||||
@@ -753,6 +755,11 @@ struct JanetReg {
|
||||
const char *documentation;
|
||||
};
|
||||
|
||||
struct JanetMethod {
|
||||
const char *name;
|
||||
JanetCFunction cfun;
|
||||
};
|
||||
|
||||
struct JanetView {
|
||||
const Janet *items;
|
||||
int32_t len;
|
||||
@@ -1002,7 +1009,6 @@ JANET_API void janet_description_b(JanetBuffer *buffer, Janet x);
|
||||
#define janet_cstringv(cstr) janet_wrap_string(janet_cstring(cstr))
|
||||
#define janet_stringv(str, len) janet_wrap_string(janet_string((str), (len)))
|
||||
JANET_API const uint8_t *janet_formatc(const char *format, ...);
|
||||
JANET_API void janet_puts(const uint8_t *str);
|
||||
|
||||
/* Symbol functions */
|
||||
JANET_API const uint8_t *janet_symbol(const uint8_t *str, int32_t len);
|
||||
@@ -1046,8 +1052,8 @@ JANET_API void janet_table_merge_struct(JanetTable *table, const JanetKV *other)
|
||||
JANET_API JanetKV *janet_table_find(JanetTable *t, Janet key);
|
||||
|
||||
/* Fiber */
|
||||
JANET_API JanetFiber *janet_fiber(JanetFunction *callee, int32_t capacity);
|
||||
JANET_API JanetFiber *janet_fiber_n(JanetFunction *callee, int32_t capacity, const Janet *argv, int32_t argn);
|
||||
JANET_API JanetFiber *janet_fiber(JanetFunction *callee, int32_t capacity, int32_t argc, const Janet *argv);
|
||||
JANET_API JanetFiber *janet_fiber_reset(JanetFiber *fiber, JanetFunction *callee, int32_t argc, const Janet *argv);
|
||||
#define janet_fiber_status(f) (((f)->flags & JANET_FIBER_STATUS_MASK) >> JANET_FIBER_STATUS_OFFSET)
|
||||
|
||||
/* Treat similar types through uniform interfaces for iteration */
|
||||
@@ -1110,14 +1116,14 @@ JANET_API Janet janet_getindex(Janet ds, int32_t index);
|
||||
JANET_API int32_t janet_length(Janet x);
|
||||
JANET_API void janet_put(Janet ds, Janet key, Janet value);
|
||||
JANET_API void janet_putindex(Janet ds, int32_t index, Janet value);
|
||||
JANET_API void janet_inspect(Janet x);
|
||||
|
||||
/* VM functions */
|
||||
JANET_API int janet_init(void);
|
||||
JANET_API void janet_deinit(void);
|
||||
JANET_API JanetSignal janet_continue(JanetFiber *fiber, Janet in, Janet *out);
|
||||
JANET_API JanetSignal janet_call(JanetFunction *fun, int32_t argn, const Janet *argv, Janet *out, JanetFiber **f);
|
||||
JANET_API void janet_stacktrace(JanetFiber *fiber, const char *errtype, Janet err);
|
||||
JANET_API JanetSignal janet_pcall(JanetFunction *fun, int32_t argn, const Janet *argv, Janet *out, JanetFiber **f);
|
||||
JANET_API Janet janet_call(JanetFunction *fun, int32_t argc, const Janet *argv);
|
||||
JANET_API void janet_stacktrace(JanetFiber *fiber, Janet err);
|
||||
|
||||
/* C Library helpers */
|
||||
typedef enum {
|
||||
@@ -1145,6 +1151,7 @@ JANET_API void janet_panic_abstract(Janet x, int32_t n, const JanetAbstractType
|
||||
JANET_API void janet_arity(int32_t arity, int32_t min, int32_t max);
|
||||
JANET_API void janet_fixarity(int32_t arity, int32_t fix);
|
||||
|
||||
JANET_API Janet janet_getmethod(const uint8_t *method, const JanetMethod *methods);
|
||||
JANET_API double janet_getnumber(const Janet *argv, int32_t n);
|
||||
JANET_API JanetArray *janet_getarray(const Janet *argv, int32_t n);
|
||||
JANET_API const Janet *janet_gettuple(const Janet *argv, int32_t n);
|
||||
@@ -1166,6 +1173,8 @@ JANET_API JanetByteView janet_getbytes(const Janet *argv, int32_t n);
|
||||
JANET_API JanetDictView janet_getdictionary(const Janet *argv, int32_t n);
|
||||
JANET_API void *janet_getabstract(const Janet *argv, int32_t n, const JanetAbstractType *at);
|
||||
JANET_API JanetRange janet_getslice(int32_t argc, const Janet *argv);
|
||||
JANET_API int32_t janet_gethalfrange(const Janet *argv, int32_t n, int32_t length, const char *which);
|
||||
JANET_API int32_t janet_getargindex(const Janet *argv, int32_t n, int32_t length, const char *which);
|
||||
|
||||
/***** END SECTION MAIN *****/
|
||||
|
||||
|
||||
@@ -2,16 +2,17 @@
|
||||
|
||||
(do
|
||||
|
||||
(var *should-repl* :private false)
|
||||
(var *no-file* :private true)
|
||||
(var *raw-stdin* :private false)
|
||||
(var *handleopts* :private true)
|
||||
(var *exit-on-error* :private true)
|
||||
(var *should-repl* false)
|
||||
(var *no-file* true)
|
||||
(var *quiet* false)
|
||||
(var *raw-stdin* false)
|
||||
(var *handleopts* true)
|
||||
(var *exit-on-error* true)
|
||||
|
||||
# Flag handlers
|
||||
(def handlers :private
|
||||
{"h" (fn [&]
|
||||
(print "usage: " (get process/args 0) " [options] scripts...")
|
||||
(print "usage: " (get process/args 0) " [options] script args...")
|
||||
(print
|
||||
`Options are:
|
||||
-h Show this help
|
||||
@@ -20,6 +21,8 @@
|
||||
-e Execute a string of janet
|
||||
-r Enter the repl after running all scripts
|
||||
-p Keep on executing if there is a top level error (persistent)
|
||||
-q Hide prompt, logo, and repl output (quiet)
|
||||
-l Execute code in a file before running the main script
|
||||
-- Stop handling options`)
|
||||
(os/exit 0)
|
||||
1)
|
||||
@@ -27,7 +30,12 @@
|
||||
"s" (fn [&] (set *raw-stdin* true) (set *should-repl* true) 1)
|
||||
"r" (fn [&] (set *should-repl* true) 1)
|
||||
"p" (fn [&] (set *exit-on-error* false) 1)
|
||||
"q" (fn [&] (set *quiet* true) 1)
|
||||
"-" (fn [&] (set *handleopts* false) 1)
|
||||
"l" (fn [i &]
|
||||
(import* *env* (get process/args (+ i 1))
|
||||
:prefix "" :exit *exit-on-error*)
|
||||
2)
|
||||
"e" (fn [i &]
|
||||
(set *no-file* false)
|
||||
(eval-string (get process/args (+ i 1)))
|
||||
@@ -46,15 +54,23 @@
|
||||
(+= i (dohandler (string/slice arg 1 2) i))
|
||||
(do
|
||||
(set *no-file* false)
|
||||
(import* _env arg :prefix "" :exit *exit-on-error*)
|
||||
(++ i))))
|
||||
(import* *env* arg :prefix "" :exit *exit-on-error*)
|
||||
(set i lenargs))))
|
||||
|
||||
(when (or *should-repl* *no-file*)
|
||||
(if *raw-stdin*
|
||||
(repl nil (fn [x &] x))
|
||||
(do
|
||||
(print (string "Janet " janet/version "-" janet/build " Copyright (C) 2017-2018 Calvin Rose"))
|
||||
(repl (fn [buf p]
|
||||
(def offset (parser/where p))
|
||||
(def prompt (string "janet:" offset ":" (parser/state p) "> "))
|
||||
(getline prompt buf)))))))
|
||||
(if-not *quiet*
|
||||
(print "Janet " janet/version "-" janet/build " Copyright (C) 2017-2019 Calvin Rose"))
|
||||
(defn noprompt [_] "")
|
||||
(defn getprompt [p]
|
||||
(def offset (parser/where p))
|
||||
(string "janet:" offset ":" (parser/state p) "> "))
|
||||
(def prompter (if *quiet* noprompt getprompt))
|
||||
(defn getstdin [prompt buf]
|
||||
(file/write stdout prompt)
|
||||
(file/flush stdout)
|
||||
(file/read stdin :line buf))
|
||||
(def getter (if *raw-stdin* getstdin getline))
|
||||
(defn getchunk [buf p]
|
||||
(getter (prompter p) buf))
|
||||
(def onsig (if *quiet* (fn [x &] x) nil))
|
||||
(repl getchunk onsig)))
|
||||
|
||||
@@ -24,11 +24,11 @@
|
||||
|
||||
/* Common */
|
||||
Janet janet_line_getter(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 2);
|
||||
const uint8_t *str = janet_getstring(argv, 0);
|
||||
JanetBuffer *buf = janet_getbuffer(argv, 1);
|
||||
janet_arity(argc, 0, 2);
|
||||
const char *str = (argc >= 1) ? (const char *) janet_getstring(argv, 0) : "";
|
||||
JanetBuffer *buf = (argc >= 2) ? janet_getbuffer(argv, 1) : janet_buffer(10);
|
||||
janet_line_get(str, buf);
|
||||
return argv[0];
|
||||
return janet_wrap_buffer(buf);
|
||||
}
|
||||
|
||||
static void simpleline(JanetBuffer *buffer) {
|
||||
@@ -55,8 +55,8 @@ void janet_line_deinit() {
|
||||
;
|
||||
}
|
||||
|
||||
void janet_line_get(const uint8_t *p, JanetBuffer *buffer) {
|
||||
fputs((const char *)p, stdout);
|
||||
void janet_line_get(const char *p, JanetBuffer *buffer) {
|
||||
fputs(p, stdout);
|
||||
simpleline(buffer);
|
||||
}
|
||||
|
||||
@@ -444,8 +444,8 @@ static int checktermsupport() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
void janet_line_get(const uint8_t *p, JanetBuffer *buffer) {
|
||||
prompt = (const char *)p;
|
||||
void janet_line_get(const char *p, JanetBuffer *buffer) {
|
||||
prompt = p;
|
||||
buffer->count = 0;
|
||||
historyi = 0;
|
||||
if (!isatty(STDIN_FILENO) || !checktermsupport()) {
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
void janet_line_init();
|
||||
void janet_line_deinit();
|
||||
|
||||
void janet_line_get(const uint8_t *p, JanetBuffer *buffer);
|
||||
void janet_line_get(const char *p, JanetBuffer *buffer);
|
||||
Janet janet_line_getter(int32_t argc, Janet *argv);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -44,7 +44,7 @@ static int enter_loop(void) {
|
||||
Janet ret;
|
||||
JanetSignal status = janet_continue(repl_fiber, janet_wrap_nil(), &ret);
|
||||
if (status == JANET_SIGNAL_ERROR) {
|
||||
janet_stacktrace(repl_fiber, "runtime", ret);
|
||||
janet_stacktrace(repl_fiber, ret);
|
||||
janet_deinit();
|
||||
repl_fiber = NULL;
|
||||
return 1;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Copyright 2017-2019 (C) Calvin Rose
|
||||
|
||||
(print (string "Janet " janet/version "-" janet/build " Copyright (C) 2017-2018 Calvin Rose"))
|
||||
(print (string "Janet " janet/version "-" janet/build " Copyright (C) 2017-2019 Calvin Rose"))
|
||||
|
||||
(fiber/new (fn webrepl []
|
||||
(repl (fn get-line [buf p]
|
||||
|
||||
@@ -21,6 +21,11 @@
|
||||
(print e)))
|
||||
x)
|
||||
|
||||
(defmacro assert-error
|
||||
[msg & forms]
|
||||
(def errsym (keyword (gensym)))
|
||||
~(assert (= ,errsym (try (do ,;forms) ([_] ,errsym))) ,msg))
|
||||
|
||||
(defn start-suite [x]
|
||||
(set suite-num x)
|
||||
(print "\nRunning test suite " x " tests...\n "))
|
||||
|
||||
@@ -283,5 +283,22 @@
|
||||
(++ i))
|
||||
(assert (= i 6) "when macro"))
|
||||
|
||||
# Denormal tables and structs
|
||||
|
||||
(assert (= (length {1 2 nil 3}) 1) "nil key struct literal")
|
||||
(assert (= (length @{1 2 nil 3}) 1) "nil key table literal")
|
||||
(assert (= (length (struct 1 2 nil 3)) 1) "nil key struct ctor")
|
||||
(assert (= (length (table 1 2 nil 3)) 1) "nil key table ctor")
|
||||
|
||||
(assert (= (length (struct (/ 0 0) 2 1 3)) 1) "nan key struct ctor")
|
||||
(assert (= (length (table (/ 0 0) 2 1 3)) 1) "nan key table ctor")
|
||||
(assert (= (length {1 2 nil 3}) 1) "nan key struct literal")
|
||||
(assert (= (length @{1 2 nil 3}) 1) "nan key table literal")
|
||||
|
||||
(assert (= (length (struct 2 1 3 nil)) 1) "nil value struct ctor")
|
||||
(assert (= (length (table 2 1 3 nil)) 1) "nil value table ctor")
|
||||
(assert (= (length {1 2 3 nil}) 1) "nil value struct literal")
|
||||
(assert (= (length @{1 2 3 nil}) 1) "nil value table literal")
|
||||
|
||||
(end-suite)
|
||||
|
||||
|
||||
@@ -140,7 +140,7 @@
|
||||
|
||||
# Marshal
|
||||
|
||||
(def um-lookup (env-lookup _env))
|
||||
(def um-lookup (env-lookup *env*))
|
||||
(def m-lookup (invert um-lookup))
|
||||
|
||||
(defn testmarsh [x msg]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2019 Calvin Rose
|
||||
#' Copyright (c) 2019 Calvin Rose
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
|
||||
@@ -53,6 +53,12 @@
|
||||
|
||||
(assert (= var-b "hello") "regression 1")
|
||||
|
||||
# Scan number
|
||||
|
||||
(assert (= 1 (scan-number "1")) "scan-number 1")
|
||||
(assert (= -1 (scan-number "-1")) "scan-number -1")
|
||||
(assert (= 1.3e4 (scan-number "1.3e4")) "scan-number 1.3e4")
|
||||
|
||||
# Some macros
|
||||
|
||||
(assert (= 2 (if-not 1 3 2)) "if-not 1")
|
||||
@@ -122,4 +128,227 @@
|
||||
(def spot (make-dog "spot"))
|
||||
(assert (= "spot says hi!" (:bark spot "hi")) "oo 2")
|
||||
|
||||
# Negative tests
|
||||
|
||||
(assert-error "+ check types" (+ 1 ()))
|
||||
(assert-error "- check types" (- 1 ()))
|
||||
(assert-error "* check types" (* 1 ()))
|
||||
(assert-error "/ check types" (/ 1 ()))
|
||||
(assert-error "band check types" (band 1 ()))
|
||||
(assert-error "bor check types" (bor 1 ()))
|
||||
(assert-error "bxor check types" (bxor 1 ()))
|
||||
(assert-error "bnot check types" (bnot ()))
|
||||
|
||||
# Buffer blitting
|
||||
|
||||
(def b (buffer/new-filled 100))
|
||||
(buffer/bit-set b 100)
|
||||
(buffer/bit-clear b 100)
|
||||
(assert (zero? (sum b)) "buffer bit set and clear")
|
||||
(buffer/bit-toggle b 101)
|
||||
(assert (= 32 (sum b)) "buffer bit set and clear")
|
||||
|
||||
(def b2 @"hello world")
|
||||
|
||||
(buffer/blit b2 "joyto ")
|
||||
(assert (= (string b2) "joyto world") "buffer/blit 1")
|
||||
|
||||
(buffer/blit b2 "joyto" 6)
|
||||
(assert (= (string b2) "joyto joyto") "buffer/blit 2")
|
||||
|
||||
(buffer/blit b2 "abcdefg" 5 6)
|
||||
(assert (= (string b2) "joytogjoyto") "buffer/blit 3")
|
||||
|
||||
# Buffer push word
|
||||
|
||||
(def b3 @"")
|
||||
(buffer/push-word b3 0xFF 0x11)
|
||||
(assert (= 8 (length b3)) "buffer/push-word 1")
|
||||
(assert (= "\xFF\0\0\0\x11\0\0\0" (string b3)) "buffer/push-word 2")
|
||||
(buffer/clear b3)
|
||||
(buffer/push-word b3 0xFFFFFFFF 0x1100)
|
||||
(assert (= 8 (length b3)) "buffer/push-word 3")
|
||||
(assert (= "\xFF\xFF\xFF\xFF\0\x11\0\0" (string b3)) "buffer/push-word 4")
|
||||
|
||||
# Peg
|
||||
|
||||
(defn check-match
|
||||
[pat text should-match]
|
||||
(def result (peg/match pat text))
|
||||
(assert (= (not should-match) (not result)) text))
|
||||
|
||||
(defn check-deep
|
||||
[pat text what]
|
||||
(def result (peg/match pat text))
|
||||
(assert (deep= result what) text))
|
||||
|
||||
# Just numbers
|
||||
|
||||
(check-match '(* 4 -1) "abcd" true)
|
||||
(check-match '(* 4 -1) "abc" false)
|
||||
(check-match '(* 4 -1) "abcde" false)
|
||||
|
||||
# Simple pattern
|
||||
|
||||
(check-match '(* (some (range "az" "AZ")) -1) "hello" true)
|
||||
(check-match '(* (some (range "az" "AZ")) -1) "hello world" false)
|
||||
(check-match '(* (some (range "az" "AZ")) -1) "1he11o" false)
|
||||
(check-match '(* (some (range "az" "AZ")) -1) "" false)
|
||||
|
||||
# Pre compile
|
||||
|
||||
(def pegleg (peg/compile '{:item "abc" :main (* :item "," :item -1)}))
|
||||
|
||||
(peg/match pegleg "abc,abc")
|
||||
|
||||
# Bad Grammars
|
||||
|
||||
(assert-error "peg/compile error 1" (peg/compile nil))
|
||||
(assert-error "peg/compile error 2" (peg/compile @{}))
|
||||
(assert-error "peg/compile error 3" (peg/compile '{:a "abc" :b "def"}))
|
||||
(assert-error "peg/compile error 4" (peg/compile '(blarg "abc")))
|
||||
(assert-error "peg/compile error 5" (peg/compile '(1 2 3)))
|
||||
|
||||
# IP address
|
||||
|
||||
(def ip-address
|
||||
'{:d (range "09")
|
||||
:0-4 (range "04")
|
||||
:0-5 (range "05")
|
||||
:byte (+
|
||||
(* "25" :0-5)
|
||||
(* "2" :0-4 :d)
|
||||
(* "1" :d :d)
|
||||
(between 1 2 :d))
|
||||
:main (* :byte "." :byte "." :byte "." :byte)})
|
||||
|
||||
(check-match ip-address "10.240.250.250" true)
|
||||
(check-match ip-address "0.0.0.0" true)
|
||||
(check-match ip-address "1.2.3.4" true)
|
||||
(check-match ip-address "256.2.3.4" false)
|
||||
(check-match ip-address "256.2.3.2514" false)
|
||||
|
||||
# Substitution test with peg
|
||||
|
||||
(file/flush stderr)
|
||||
(file/flush stdout)
|
||||
|
||||
(def grammar '(accumulate (any (+ (/ "dog" "purple panda") (<- 1)))))
|
||||
(defn try-grammar [text]
|
||||
(assert (= (string/replace-all "dog" "purple panda" text) (0 (peg/match grammar text))) text))
|
||||
|
||||
(try-grammar "i have a dog called doug the dog. he is good.")
|
||||
(try-grammar "i have a dog called doug the dog. he is a good boy.")
|
||||
(try-grammar "i have a dog called doug the do")
|
||||
(try-grammar "i have a dog called doug the dog")
|
||||
(try-grammar "i have a dog called doug the dogg")
|
||||
(try-grammar "i have a dog called doug the doggg")
|
||||
(try-grammar "i have a dog called doug the dogggg")
|
||||
|
||||
# Peg CSV test
|
||||
|
||||
(def csv
|
||||
'{:field (+
|
||||
(* `"` (% (any (+ (<- (if-not `"` 1)) (* (constant `"`) `""`)))) `"`)
|
||||
(<- (any (if-not (set ",\n") 1))))
|
||||
:main (* :field (any (* "," :field)) (+ "\n" -1))})
|
||||
|
||||
(defn check-csv
|
||||
[str res]
|
||||
(check-deep csv str res))
|
||||
|
||||
(check-csv "1,2,3" @["1" "2" "3"])
|
||||
(check-csv "1,\"2\",3" @["1" "2" "3"])
|
||||
(check-csv ``1,"1""",3`` @["1" "1\"" "3"])
|
||||
|
||||
# Nested Captures
|
||||
|
||||
(def grmr '(capture (* (capture "a") (capture 1) (capture "c"))))
|
||||
(check-deep grmr "abc" @["a" "b" "c" "abc"])
|
||||
(check-deep grmr "acc" @["a" "c" "c" "acc"])
|
||||
|
||||
# Functions in grammar
|
||||
|
||||
(def grmr-triple ~(% (any (/ (<- 1) ,(fn [x] (string x x x))))))
|
||||
(check-deep grmr-triple "abc" @["aaabbbccc"])
|
||||
(check-deep grmr-triple "" @[""])
|
||||
(check-deep grmr-triple " " @[" "])
|
||||
|
||||
(def counter ~(/ (group (any (<- 1))) ,length))
|
||||
(check-deep counter "abcdefg" @[7])
|
||||
|
||||
# Capture Backtracking
|
||||
|
||||
(check-deep '(+ (* (capture "c") "d") "ce") "ce" @[])
|
||||
|
||||
# Matchtime capture
|
||||
|
||||
(def scanner (peg/compile ~(cmt (capture (some 1)) ,scan-number)))
|
||||
|
||||
(check-deep scanner "123" @[123])
|
||||
(check-deep scanner "0x86" @[0x86])
|
||||
(check-deep scanner "-1.3e-7" @[-1.3e-7])
|
||||
(check-deep scanner "123A" nil)
|
||||
|
||||
# Recursive grammars
|
||||
|
||||
(def g '{:main (+ (* "a" :main "b") "c")})
|
||||
|
||||
(check-match g "c" true)
|
||||
(check-match g "acb" true)
|
||||
(check-match g "aacbb" true)
|
||||
(check-match g "aadbb" false)
|
||||
|
||||
# Back reference
|
||||
|
||||
(def wrapped-string
|
||||
~{:pad (any "=")
|
||||
:open (* "[" (<- :pad :n) "[")
|
||||
:close (* "]" (cmt (* (-> :n) (<- :pad)) ,=) "]")
|
||||
:main (* :open (any (if-not :close 1)) :close -1)})
|
||||
|
||||
(check-match wrapped-string "[[]]" true)
|
||||
(check-match wrapped-string "[==[a]==]" true)
|
||||
(check-match wrapped-string "[==[]===]" false)
|
||||
(check-match wrapped-string "[[blark]]" true)
|
||||
(check-match wrapped-string "[[bl[ark]]" true)
|
||||
(check-match wrapped-string "[[bl]rk]]" true)
|
||||
(check-match wrapped-string "[[bl]rk]] " false)
|
||||
(check-match wrapped-string "[=[bl]]rk]=] " false)
|
||||
(check-match wrapped-string "[=[bl]==]rk]=] " false)
|
||||
(check-match wrapped-string "[===[]==]===]" true)
|
||||
|
||||
(def janet-longstring
|
||||
~{:delim (some "`")
|
||||
:open (capture :delim :n)
|
||||
:close (cmt (* (not (> -1 "`")) (-> :n) (<- :delim)) ,=)
|
||||
:main (* :open (any (if-not :close 1)) :close -1)})
|
||||
|
||||
(check-match janet-longstring "`john" false)
|
||||
(check-match janet-longstring "abc" false)
|
||||
(check-match janet-longstring "` `" true)
|
||||
(check-match janet-longstring "` `" true)
|
||||
(check-match janet-longstring "`` ``" true)
|
||||
(check-match janet-longstring "``` `` ```" true)
|
||||
(check-match janet-longstring "`` ```" false)
|
||||
|
||||
# Optional
|
||||
|
||||
(check-match '(* (opt "hi") -1) "" true)
|
||||
(check-match '(* (opt "hi") -1) "hi" true)
|
||||
(check-match '(* (opt "hi") -1) "no" false)
|
||||
(check-match '(* (? "hi") -1) "" true)
|
||||
(check-match '(* (? "hi") -1) "hi" true)
|
||||
(check-match '(* (? "hi") -1) "no" false)
|
||||
|
||||
# Drop
|
||||
|
||||
(check-deep '(drop '"hello") "hello" @[])
|
||||
(check-deep '(drop "hello") "hello" @[])
|
||||
|
||||
# Regression #24
|
||||
|
||||
(def t (put @{} :hi 1))
|
||||
(assert (deep= t @{:hi 1}) "regression #24")
|
||||
|
||||
(end-suite)
|
||||
|
||||
76
tools/amalg.janet
Normal file
76
tools/amalg.janet
Normal file
@@ -0,0 +1,76 @@
|
||||
# Creates an amalgamated janet.c and janet.h to
|
||||
# allow for easy embedding
|
||||
|
||||
(def {:year YY :month MM :month-day DD} (os/date))
|
||||
|
||||
(defn dofile
|
||||
"Print one file to stdout"
|
||||
[path]
|
||||
(print (slurp path)))
|
||||
|
||||
# Order is important here, as some headers
|
||||
# depend on other headers.
|
||||
(def headers
|
||||
@["src/core/util.h"
|
||||
"src/core/state.h"
|
||||
"src/core/gc.h"
|
||||
"src/core/vector.h"
|
||||
"src/core/fiber.h"
|
||||
"src/core/regalloc.h"
|
||||
"src/core/compile.h"
|
||||
"src/core/emit.h"
|
||||
"src/core/symcache.h"])
|
||||
|
||||
(def sources
|
||||
@["src/core/abstract.c"
|
||||
"src/core/array.c"
|
||||
"src/core/asm.c"
|
||||
"src/core/buffer.c"
|
||||
"src/core/bytecode.c"
|
||||
"src/core/capi.c"
|
||||
"src/core/cfuns.c"
|
||||
"src/core/compile.c"
|
||||
"src/core/corelib.c"
|
||||
"src/core/debug.c"
|
||||
"src/core/emit.c"
|
||||
"src/core/fiber.c"
|
||||
"src/core/gc.c"
|
||||
"src/core/io.c"
|
||||
"src/core/marsh.c"
|
||||
"src/core/math.c"
|
||||
"src/core/os.c"
|
||||
"src/core/parse.c"
|
||||
"src/core/peg.c"
|
||||
"src/core/pp.c"
|
||||
"src/core/regalloc.c"
|
||||
"src/core/run.c"
|
||||
"src/core/specials.c"
|
||||
"src/core/string.c"
|
||||
"src/core/strtod.c"
|
||||
"src/core/struct.c"
|
||||
"src/core/symcache.c"
|
||||
"src/core/table.c"
|
||||
"src/core/tuple.c"
|
||||
"src/core/util.c"
|
||||
"src/core/value.c"
|
||||
"src/core/vector.c"
|
||||
"src/core/vm.c"
|
||||
"src/core/wrap.c"])
|
||||
|
||||
(print "/* Amalgamated build - DO NOT EDIT */")
|
||||
(print "/* Generated " YY "-" (inc MM) "-" (inc DD)
|
||||
" with janet version " janet/version "-" janet/build " */")
|
||||
|
||||
# Assume the version of janet used to run this script is the same
|
||||
# as the version being generated
|
||||
(print "#define JANET_BUILD \"" janet/build "\"")
|
||||
|
||||
(print ```#define JANET_AMALG```)
|
||||
(print ```#include "janet.h"```)
|
||||
|
||||
(each h headers (dofile h))
|
||||
(each s sources (dofile s))
|
||||
|
||||
# Relies on these files being built
|
||||
(dofile "build/core.gen.c")
|
||||
(dofile "build/core_image.c")
|
||||
55
tools/bars.janet
Normal file
55
tools/bars.janet
Normal file
@@ -0,0 +1,55 @@
|
||||
# A flexible templater for janet. Compiles
|
||||
# templates to janet functions that produce buffers.
|
||||
|
||||
(defn template
|
||||
"Compile a template string into a function"
|
||||
[source]
|
||||
|
||||
# State for compilation machine
|
||||
(def p (parser/new))
|
||||
(def forms @[])
|
||||
|
||||
(defn parse-chunk
|
||||
"Parse a string and push produced values to forms."
|
||||
[chunk]
|
||||
(parser/consume p chunk)
|
||||
(while (parser/has-more p)
|
||||
(array/push forms (parser/produce p)))
|
||||
(if (= :error (parser/status p))
|
||||
(error (parser/error p))))
|
||||
|
||||
(defn code-chunk
|
||||
"Parse all the forms in str and return them
|
||||
in a tuple prefixed with 'do."
|
||||
[str]
|
||||
(parse-chunk str)
|
||||
true)
|
||||
|
||||
(defn string-chunk
|
||||
"Insert string chunk into parser"
|
||||
[str]
|
||||
(parser/insert p str)
|
||||
(parse-chunk "")
|
||||
true)
|
||||
|
||||
# Run peg
|
||||
(def grammar
|
||||
~{:code-chunk (* "{%" (drop (cmt '(any (if-not "%}" 1)) ,code-chunk)) "%}")
|
||||
:main-chunk (drop (cmt '(any (if-not "{%" 1)) ,string-chunk))
|
||||
:main (any (+ :code-chunk :main-chunk (error "")))})
|
||||
(def parts (peg/match grammar source))
|
||||
|
||||
# Check errors in template and parser
|
||||
(unless parts (error "invalid template syntax"))
|
||||
(parse-chunk "\n")
|
||||
(case (parser/status p)
|
||||
:pending (error (string "unfinished parser state " (parser/state p)))
|
||||
:error (error (parser/error p)))
|
||||
|
||||
# Make ast from forms
|
||||
(def ast ~(fn [params &] (default params @{}) (,buffer ;forms)))
|
||||
|
||||
(def ctor (compile ast *env* source))
|
||||
(if-not (function? ctor)
|
||||
(error (string "could not compile template")))
|
||||
(ctor))
|
||||
@@ -34,7 +34,7 @@
|
||||
[f1 f2]
|
||||
"Check if f1 is newer than f2. Used for checking if a file should be updated."
|
||||
(if is-win true
|
||||
(zero? (os/shell (string "[ " f1 " -ot " f2 " ]")))))
|
||||
(not (zero? (os/shell (string "[ " f1 " -nt " f2 " ]"))))))
|
||||
|
||||
(defn- older-than-some
|
||||
[f others]
|
||||
@@ -79,14 +79,14 @@
|
||||
(defn- make-define
|
||||
"Generate strings for adding custom defines to the compiler."
|
||||
[define value]
|
||||
(def prefix (if is-win "\\D" "-D"))
|
||||
(def prefix (if is-win "/D" "-D"))
|
||||
(if value
|
||||
(string prefix define "=" value)
|
||||
(string prefix define)))
|
||||
|
||||
(defn- make-defines
|
||||
"Generate many defines. Takes a dictionary of defines. If a value is
|
||||
true, generates -DNAME (\\DNAME on windows), otherwise -DNAME=value."
|
||||
true, generates -DNAME (/DNAME on windows), otherwise -DNAME=value."
|
||||
[defines]
|
||||
(seq [[d v] :pairs defines] (make-define d (if (not= v true) v))))
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
(def OPTIMIZE 2)
|
||||
(def CC (if is-win "cl" "cc"))
|
||||
(def LD (if is-win "link" (string CC " -shared")))
|
||||
(def CFLAGS (string (if is-win "/0" "-std=c99 -Wall -Wextra -fpic -O") OPTIMIZE))
|
||||
(def CFLAGS (string (if is-win "/O" "-std=c99 -Wall -Wextra -fpic -O") OPTIMIZE))
|
||||
|
||||
(defn- compile-c
|
||||
"Compile a C file into an object file."
|
||||
@@ -105,18 +105,19 @@
|
||||
(if (older-than dest src)
|
||||
(if is-win
|
||||
(shell cc " " ;defines " /nologo /c " cflags " /Fo" dest " " src)
|
||||
(shell cc " " ;defines " " cflags " -o " dest " -c " src))))
|
||||
(shell cc " -c " src " " ;defines " " cflags " -o " dest))))
|
||||
|
||||
(defn- link-c
|
||||
"Link a number of object files together."
|
||||
[opts target & objects]
|
||||
(def ld (or (opts :linker) LD))
|
||||
(def cflags (or (opts :cflags) CFLAGS))
|
||||
(def lflags (or (opts :lflags) ""))
|
||||
(def olist (string/join objects " "))
|
||||
(if (older-than-some target objects)
|
||||
(if is-win
|
||||
(shell ld "/out:" target " " olist)
|
||||
(shell ld " " cflags " -o " target " " olist))))
|
||||
(shell ld " /DLL /OUT:" target " " olist " %JANET_PATH%\\janet.lib")
|
||||
(shell ld " " cflags " -o " target " " olist " " lflags))))
|
||||
|
||||
(defn- create-buffer-c
|
||||
"Inline raw byte file as a c file."
|
||||
|
||||
@@ -75,6 +75,7 @@
|
||||
:ref ref
|
||||
:source-map sm
|
||||
:doc docstring} env-entry
|
||||
html-key (html-escape key)
|
||||
binding-type (cond
|
||||
macro :macro
|
||||
ref (string :var " (" (type (get ref 0)) ")")
|
||||
@@ -82,14 +83,14 @@
|
||||
source-ref (if-let [[path start end] sm]
|
||||
(string "<span class=\"source-map\">" path " (" start ":" end ")</span>")
|
||||
"")]
|
||||
(string "<h2 class=\"binding\">" (html-escape key) "</h2>\n"
|
||||
(string "<h2 class=\"binding\"><a id=\"" key "\">" html-key "</a></h2>\n"
|
||||
"<span class=\"binding-type\">" binding-type "</span>\n"
|
||||
"<p class=\"docstring\">" (trim-lead (html-escape docstring)) "</p>\n"
|
||||
source-ref)))
|
||||
|
||||
# Generate parts and print them to stdout
|
||||
(def parts (seq [[k entry]
|
||||
:in (sort (pairs (table/getproto _env)))
|
||||
:in (sort (pairs (table/getproto *env*)))
|
||||
:when (and (get entry :doc) (not (get entry :private)))]
|
||||
(emit-item k entry)))
|
||||
(print
|
||||
|
||||
197
tools/highlight.janet
Normal file
197
tools/highlight.janet
Normal file
@@ -0,0 +1,197 @@
|
||||
# Copyright (C) Calvin Rose 2019
|
||||
#
|
||||
# Takes in a janet string and colorizes for multiple
|
||||
# output formats.
|
||||
|
||||
# Constants for checking if symbols should be
|
||||
# highlighted.
|
||||
(def- core-env (table/getproto *env*))
|
||||
(def- specials {'fn true
|
||||
'var true
|
||||
'do true
|
||||
'while true
|
||||
'def true
|
||||
'splice true
|
||||
'set true
|
||||
'unquote true
|
||||
'quasiquote true
|
||||
'quote true
|
||||
'if true})
|
||||
|
||||
(defn check-number [text] (and (scan-number text) text))
|
||||
|
||||
(defn- make-grammar
|
||||
"Creates the grammar based on the paint function, which
|
||||
colorizes fragments of text."
|
||||
[paint]
|
||||
|
||||
(defn <-c
|
||||
"Peg rule for capturing and coloring a rule."
|
||||
[color what]
|
||||
~(/ (<- ,what) ,(partial paint color)))
|
||||
|
||||
(defn color-symbol
|
||||
"Color a symbol only if it is a core library binding or special."
|
||||
[text]
|
||||
(def sym (symbol text))
|
||||
(def should-color (or (specials sym) (core-env sym)))
|
||||
(paint (if should-color :coresym :symbol) text))
|
||||
|
||||
~{:ws (set " \t\r\f\n\0")
|
||||
:readermac (set "';~,")
|
||||
:symchars (+ (range "09" "AZ" "az" "\x80\xFF") (set "!$%&*+-./:<?=>@^_|"))
|
||||
:token (some :symchars)
|
||||
:hex (range "09" "af" "AF")
|
||||
:escape (* "\\" (+ (set "ntrzf0\"\\e")
|
||||
(* "x" :hex :hex)
|
||||
(error (constant "bad hex escape"))))
|
||||
|
||||
:comment ,(<-c :comment ~(* "#" (any (if-not (+ "\n" -1) 1))))
|
||||
|
||||
:symbol (/ ':token ,color-symbol)
|
||||
:keyword ,(<-c :keyword ~(* ":" (any :symchars)))
|
||||
:constant ,(<-c :constant ~(+ "true" "false" "nil"))
|
||||
:bytes (* "\"" (any (+ :escape (if-not "\"" 1))) "\"")
|
||||
:string ,(<-c :string :bytes)
|
||||
:buffer ,(<-c :string ~(* "@" :bytes))
|
||||
:long-bytes {:delim (some "`")
|
||||
:open (capture :delim :n)
|
||||
:close (cmt (* (not (> -1 "`")) (-> :n) ':delim) ,=)
|
||||
:main (drop (* :open (any (if-not :close 1)) :close))}
|
||||
:long-string ,(<-c :string :long-bytes)
|
||||
:long-buffer ,(<-c :string ~(* "@" :long-bytes))
|
||||
:number (/ (cmt ':token ,check-number) ,(partial paint :number))
|
||||
|
||||
:raw-value (+ :comment :constant :number :keyword
|
||||
:string :buffer :long-string :long-buffer
|
||||
:parray :barray :ptuple :btuple :struct :dict :symbol)
|
||||
|
||||
:value (* (? '(some (+ :ws :readermac))) :raw-value '(any :ws))
|
||||
:root (any :value)
|
||||
:root2 (any (* :value :value))
|
||||
:ptuple (* '"(" :root (+ '")" (error "")))
|
||||
:btuple (* '"[" :root (+ '"]" (error "")))
|
||||
:struct (* '"{" :root2 (+ '"}" (error "")))
|
||||
:parray (* '"@" :ptuple)
|
||||
:barray (* '"@" :btuple)
|
||||
:dict (* '"@" :struct)
|
||||
|
||||
:main (+ (% :root) (error ""))})
|
||||
|
||||
# Terminal syntax highlighting
|
||||
|
||||
(def- terminal-colors
|
||||
{:number 32
|
||||
:keyword 33
|
||||
:string 35
|
||||
:coresym 31
|
||||
:constant 34
|
||||
:comment 36})
|
||||
|
||||
(defn- terminal-paint
|
||||
"Paint colors for ansi terminals"
|
||||
[what str]
|
||||
(def code (get terminal-colors what))
|
||||
(if code (string "\e[" code "m" str "\e[0m") str))
|
||||
|
||||
# HTML syntax highlighting
|
||||
|
||||
(def- html-colors
|
||||
{:number "j-number"
|
||||
:keyword "j-keyword"
|
||||
:string "j-string"
|
||||
:coresym "j-coresym"
|
||||
:constant "j-constant"
|
||||
:comment "j-comment"
|
||||
:line "j-line"})
|
||||
|
||||
(def- escapes
|
||||
{38 "&"
|
||||
60 "<"
|
||||
62 ">"
|
||||
34 """
|
||||
39 "'"
|
||||
47 "/"})
|
||||
|
||||
(def html-style
|
||||
"Style tag to add to a page to highlight janet code"
|
||||
```
|
||||
<style type="text/css">
|
||||
.j-main { color: white; background: #111; font-size: 1.4em; }
|
||||
.j-number { color: #89dc76; }
|
||||
.j-keyword { color: #ffd866; }
|
||||
.j-string { color: #ab90f2; }
|
||||
.j-coresym { color: #ff6188; }
|
||||
.j-constant { color: #fc9867; }
|
||||
.j-comment { color: darkgray; }
|
||||
.j-line { color: gray; }
|
||||
</style>
|
||||
```)
|
||||
|
||||
(defn html-escape
|
||||
"Escape special characters for HTML encoding."
|
||||
[str]
|
||||
(def buf @"")
|
||||
(loop [byte :in str]
|
||||
(if-let [rep (get escapes byte)]
|
||||
(buffer/push-string buf rep)
|
||||
(buffer/push-byte buf byte)))
|
||||
buf)
|
||||
|
||||
(defn- html-paint
|
||||
"Paint colors for HTML"
|
||||
[what str]
|
||||
(def color (get html-colors what))
|
||||
(def escaped (html-escape str))
|
||||
(if color
|
||||
(string "<span class=\"" color "\">" escaped "</span>")
|
||||
escaped))
|
||||
|
||||
# Create Pegs
|
||||
|
||||
(def- terminal-grammar (peg/compile (make-grammar terminal-paint)))
|
||||
(def- html-grammar (peg/compile (make-grammar html-paint)))
|
||||
|
||||
# API
|
||||
|
||||
(defn ansi
|
||||
"Highlight janet source code ANSI Termianl escape colors."
|
||||
[source]
|
||||
(0 (peg/match terminal-grammar source)))
|
||||
|
||||
(defn html
|
||||
"Highlight janet source code and output HTML."
|
||||
[source]
|
||||
(string "<pre class=\"j-main\"><code>"
|
||||
(0 (peg/match html-grammar source))
|
||||
"</code></pre>"))
|
||||
|
||||
(defn html-file
|
||||
"Highlight a janet file and print out a highlighted HTML version
|
||||
of the file. Must provide a default title when creating the file."
|
||||
[in-path out-path title &]
|
||||
(default title in-path)
|
||||
(def f (file/open in-path :r))
|
||||
(def source (file/read f :all))
|
||||
(file/close f)
|
||||
(def markup (0 (peg/match html-grammar source)))
|
||||
(def out (file/open out-path :w))
|
||||
(file/write out
|
||||
"<!doctype html><html><head><meta charset=\"UTF-8\">"
|
||||
html-style
|
||||
"<title>"
|
||||
title
|
||||
"</title></head>"
|
||||
"<body class=\"j-main\"><pre>"
|
||||
markup
|
||||
"</pre></body></html>")
|
||||
(file/close out))
|
||||
|
||||
(defn ansi-file
|
||||
"Highlight a janet file and print the highlighted output to stdout."
|
||||
[in-path]
|
||||
(def f (file/open in-path :r))
|
||||
(def source (file/read f :all))
|
||||
(file/close f)
|
||||
(def markup (0 (peg/match terminal-grammar source)))
|
||||
(print markup))
|
||||
@@ -1,21 +0,0 @@
|
||||
# Tool to dump a marshalled version of the janet core to stdout. The
|
||||
# image should eventually allow janet to be started from a pre-compiled
|
||||
# image rather than recompiled every time from the embedded source. More
|
||||
# work will go into shrinking the image (it isn't currently that large but
|
||||
# could be smaller), creating the mechanism to load the image, and modifying
|
||||
# the build process to compile janet with a built image rather than
|
||||
# embedded source.
|
||||
|
||||
# Get image. This image contains as much of the core library and documentation that
|
||||
# can be written to an image (no cfunctions, no abstracts (stdout, stdin, stderr)),
|
||||
# everything else goes. Cfunctions and abstracts will be referenced from a register
|
||||
# table which will be generated on janet startup.
|
||||
(def image (let [env-pairs (pairs (env-lookup _env))
|
||||
essential-pairs (filter (fn [[k v]] (or (cfunction? v) (abstract? v))) env-pairs)
|
||||
lookup (table ;(mapcat identity essential-pairs))
|
||||
reverse-lookup (invert lookup)]
|
||||
(marshal (table/getproto _env) reverse-lookup)))
|
||||
|
||||
# Write image
|
||||
(file/write stdout image)
|
||||
(file/flush stdout)
|
||||
@@ -1,3 +1,9 @@
|
||||
# Helper to generate core library mappings for janet
|
||||
# Used to help build the tmLanguage grammar. Emits
|
||||
# the entire .tmLanguage file for janet.
|
||||
|
||||
(def grammar-template
|
||||
`````
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
@@ -216,7 +222,7 @@
|
||||
<key>corelib</key>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>(?<![\.:\w_\-=!@\$%^&?|\\/<>*])(%|%=|\*|\*=|\*doc\-width\*|\*env\*|\+|\+\+|\+=|\-|\-\-|\-=|\->|\->>|\-\?>|\-\?>>|/|/=|<|<=|=|==|>|>=|_env|abstract\?|all|all\-symbols|allsyms|and|apply|array|array/concat|array/ensure|array/insert|array/new|array/peek|array/pop|array/push|array/slice|array\?|as\->|as\?\->|asm|band|blshift|bnot|boolean\?|bor|brshift|brushift|buffer|buffer/clear|buffer/new|buffer/popn|buffer/push\-byte|buffer/push\-string|buffer/push\-word|buffer/slice|buffer\?|bxor|bytes\?|callable\?|case|cfunction\?|comment|comp|compile|complement|cond|coro|count|debug|debug/arg\-stack|debug/break|debug/fbreak|debug/lineage|debug/stack|debug/unbreak|debug/unfbreak|dec|deep\-not=|deep=|def\-|default|defglobal|defmacro|defmacro\-|defn|defn\-|describe|dictionary\?|disasm|distinct|doc|doc\*|doc\-format|drop\-until|drop\-while|each|empty\?|env\-lookup|error|eval|eval\-string|even\?|every\?|extreme|false\?|fiber/current|fiber/maxstack|fiber/new|fiber/setmaxstack|fiber/status|fiber\?|file/close|file/flush|file/open|file/popen|file/read|file/seek|file/write|filter|find|find\-index|first|flatten|flatten\-into|for|frequencies|function\?|gccollect|gcinterval|gcsetinterval|generate|gensym|get|getline|hash|idempotent\?|identity|if\-let|if\-not|import|import\*|inc|indexed\?|interleave|interpose|invert|janet/build|janet/version|juxt|juxt\*|keep|keys|keyword|keyword\?|kvs|last|length|let|loop|macex|macex1|make\-env|map|mapcat|marshal|match|match\-1|math/acos|math/asin|math/atan|math/ceil|math/cos|math/e|math/exp|math/floor|math/inf|math/log|math/log10|math/pi|math/pow|math/random|math/seedrandom|math/sin|math/sqrt|math/tan|max|max\-order|merge|merge\-into|min|min\-order|module/find|module/native\-paths|module/paths|native|neg\?|next|nil\?|not|not=|not==|number\?|odd\?|one\?|or|order<|order<=|order>|order>=|os/clock|os/cwd|os/execute|os/exit|os/getenv|os/setenv|os/shell|os/sleep|os/time|os/which|pairs|parser/byte|parser/consume|parser/error|parser/flush|parser/has\-more|parser/new|parser/produce|parser/state|parser/status|parser/where|partial|pos\?|postwalk|prewalk|print|process/args|product|put|range|reduce|repl|require|resume|reverse|run\-context|scan\-number|sentinel|seq|some|sort|sorted|status\-pp|stderr|stdin|stdout|string|string/ascii\-lower|string/ascii\-upper|string/bytes|string/check\-set|string/find|string/find\-all|string/from\-bytes|string/join|string/number|string/pretty|string/repeat|string/replace|string/replace\-all|string/reverse|string/slice|string/split|string\?|struct|struct\?|sum|symbol|symbol\?|table|table/getproto|table/new|table/rawget|table/setproto|table/to\-struct|table\?|take\-until|take\-while|true\?|try|tuple|tuple/append|tuple/prepend|tuple/slice|tuple\?|type|unless|unmarshal|update|values|varglobal|walk|when|when\-let|with\-idemp|yield|zero\?|zipcoll)(?![\.:\w_\-=!@\$%^&?|\\/<>*])</string>
|
||||
<string>(?<![\.:\w_\-=!@\$%^&?|\\/<>*])(%ALLSYMBOLS%)(?![\.:\w_\-=!@\$%^&?|\\/<>*])</string>
|
||||
<key>name</key>
|
||||
<string>keyword.control.janet</string>
|
||||
</dict>
|
||||
@@ -333,3 +339,50 @@
|
||||
<string>3743190f-20c4-44d0-8640-6611a983296b</string>
|
||||
</dict>
|
||||
</plist>
|
||||
`````)
|
||||
|
||||
# Now we generate the bindings in the language.
|
||||
|
||||
(def- specials
|
||||
@["def"
|
||||
"do"
|
||||
"var"
|
||||
"set"
|
||||
"fn"
|
||||
"while"
|
||||
"if"
|
||||
"quote"
|
||||
"quasiquote"
|
||||
"unquote"
|
||||
"splice"])
|
||||
|
||||
(def allsyms (array/concat @[] specials (all-bindings)))
|
||||
|
||||
(def- escapes
|
||||
{(get "|" 0) `\|`
|
||||
(get "-" 0) `\-`
|
||||
(get "+" 0) `\+`
|
||||
(get "*" 0) `\*`
|
||||
(get "^" 0) `\^`
|
||||
(get "$" 0) `\$`
|
||||
(get "?" 0) `\?`
|
||||
38 "&"
|
||||
60 "<"
|
||||
62 ">"
|
||||
34 """
|
||||
39 "'"
|
||||
47 "/"})
|
||||
|
||||
(defn- escape
|
||||
"Escape special characters for HTML and regex encoding."
|
||||
[str]
|
||||
(def buf @"")
|
||||
(loop [byte :in str]
|
||||
(if-let [rep (get escapes byte)]
|
||||
(buffer/push-string buf rep)
|
||||
(buffer/push-byte buf byte)))
|
||||
buf)
|
||||
|
||||
(def pattern (string/join (map escape allsyms) "|"))
|
||||
|
||||
(print (string/replace "%ALLSYMBOLS%" pattern grammar-template))
|
||||
@@ -1,31 +0,0 @@
|
||||
# Helper to generate core library mappings for janet
|
||||
# Used to help build the tmLanguage grammar.
|
||||
|
||||
(def allsyms (all-symbols))
|
||||
|
||||
(def- escapes
|
||||
{(get "|" 0) `\|`
|
||||
(get "-" 0) `\-`
|
||||
(get "+" 0) `\+`
|
||||
(get "*" 0) `\*`
|
||||
(get "^" 0) `\^`
|
||||
(get "$" 0) `\$`
|
||||
(get "?" 0) `\?`
|
||||
38 "&"
|
||||
60 "<"
|
||||
62 ">"
|
||||
34 """
|
||||
39 "'"
|
||||
47 "/"})
|
||||
|
||||
(defn- escape
|
||||
"Escape special characters for HTML and regex encoding."
|
||||
[str]
|
||||
(def buf @"")
|
||||
(loop [byte :in str]
|
||||
(if-let [rep (get escapes byte)]
|
||||
(buffer/push-string buf rep)
|
||||
(buffer/push-byte buf byte)))
|
||||
buf)
|
||||
|
||||
(print (string/join (map escape allsyms) "|"))
|
||||
@@ -60,7 +60,7 @@ int main(int argc, const char **argv) {
|
||||
|
||||
/* Write the header */
|
||||
fprintf(out, "/* Auto generated - DO NOT EDIT */\n\n#include <stdint.h>\n\n");
|
||||
fprintf(out, "static const unsigned char bytes[] = {");
|
||||
fprintf(out, "static const unsigned char bytes_%s[] = {", argv[3]);
|
||||
|
||||
/* Read in chunks from buffer */
|
||||
while ((bytesRead = fread(buf, 1, sizeof(buf), in)) > 0) {
|
||||
@@ -91,7 +91,7 @@ int main(int argc, const char **argv) {
|
||||
/* Write the tail */
|
||||
fputs("\n};\n\n", out);
|
||||
|
||||
fprintf(out, "const unsigned char *%s = bytes;\n\n", argv[3]);
|
||||
fprintf(out, "const unsigned char *%s = bytes_%s;\n\n", argv[3], argv[3]);
|
||||
|
||||
/* Write chunk size */
|
||||
fprintf(out, "int32_t %s_size = %d;\n", argv[3], totalRead);
|
||||
|
||||
Reference in New Issue
Block a user