1
0
mirror of https://github.com/janet-lang/janet synced 2025-10-28 22:27:41 +00:00

Compare commits

...

44 Commits

Author SHA1 Message Date
Calvin Rose
0817e627ee Prepare for 1.24.1 release. 2022-08-24 13:23:53 -05:00
Calvin Rose
14d90239a7 Merge branch 'master' of github.com:janet-lang/janet 2022-08-24 11:35:37 -05:00
Calvin Rose
f5d11dc656 Address #1014 improve parse errors when bad delimiters are found.
Reuse some existing logic for eof errors.
2022-08-24 11:34:59 -05:00
Calvin Rose
6dcf5bf077 Merge pull request #1012 from Techcable/doc/clarify-flag-E
Clarify the documentation of janet -E flag
2022-08-21 13:45:17 -05:00
Calvin Rose
ac2082e9b3 Allow adding name to short-fns.
When short-fn is used in a macro, it can be useful to
give the function a nicer name then a raw pointer.
2022-08-18 14:33:59 -05:00
Techcable
dbac495bee Clarify the documentation of janet -E flag
This confused me, despite having a fair deal of janet experience.
2022-08-18 12:16:14 -07:00
Calvin Rose
fe5ccb163e Merge branch 'master' of github.com:janet-lang/janet 2022-08-16 12:38:59 -05:00
Calvin Rose
1aea5ee007 Remove stack inversion code for sysv64 FFI. 2022-08-16 12:38:44 -05:00
Calvin Rose
13cd9f8067 Remove stack inversion code for sysv64 FFI. 2022-08-16 12:20:38 -05:00
bakpakin
34496ecaf0 Prepare for 1.24.0 release. 2022-08-14 20:20:09 -05:00
bakpakin
c043b1d949 Add win32 ffi example. 2022-08-14 15:40:09 -05:00
bakpakin
9a6d2a7b32 Fix FFI for reference return values and stack parameter passing. 2022-08-14 15:20:30 -05:00
bakpakin
f8a9efa8e4 Allow binding pre-loaded symbols in windows FFI.
Mimic the posix RTLD_NOW setting for dlopen by iterating
opened DLLs to look for symbols.
2022-08-14 13:26:13 -05:00
Calvin Rose
5b2169e0d1 Fix docstring. 2022-08-02 14:58:32 -05:00
Calvin Rose
2c927ea768 Add testcase for issue #1005 2022-08-02 12:34:24 -05:00
Calvin Rose
f4bbcdcbc8 Get rid of disabled tracing. #1005 2022-08-02 12:19:22 -05:00
Calvin Rose
79c375b1af Address #1005 - Fix janet_call stack clobbering on dirty stack. 2022-08-02 12:13:56 -05:00
Calvin Rose
f443a3b3a1 Remove type_array option to meson_min build 2022-07-26 14:27:22 -05:00
Calvin Rose
684d2d63f4 Emphasize the ldconfig error is expected. 2022-07-20 11:19:09 -05:00
Calvin Rose
1900d8f843 Fix build warnings on Linux GCC version 12.1.0 x64 2022-07-20 08:04:03 -05:00
Calvin Rose
3c2af95d21 Update CHANGELOG.md 2022-07-19 20:05:21 -05:00
Calvin Rose
b35414ea0f Merge branch 'master' of github.com:janet-lang/janet 2022-07-19 20:04:44 -05:00
Calvin Rose
fb5b056f7b Address #1001 - don't process names passed to dlopen. 2022-07-19 20:04:17 -05:00
bakpakin
7248c1dfdb Give up if ln fail. 2022-07-09 19:10:25 -05:00
bakpakin
4c7ea9e893 Merge branch 'master' of github.com:janet-lang/janet 2022-07-09 11:44:20 -05:00
bakpakin
c7801ce277 Address #997 - clang undefined behavior warning. 2022-07-09 11:43:51 -05:00
Calvin Rose
f741a8e3ff Merge pull request #998 from autumnull/master
stop doc-format detecting other modes within code blocks
2022-07-09 11:40:51 -05:00
bakpakin
6a92e8b609 Update CHANGELOG and make tweaks to win32 shell 2022-07-09 11:39:06 -05:00
bakpakin
9da91a8217 Update shell.c to have smart behavior on windows. 2022-07-09 11:23:02 -05:00
bakpakin
69853c8e5c Merge branch 'master' of github.com:janet-lang/janet 2022-07-08 09:49:56 -05:00
Autumn!
1f41b6c138 doc-format no longer detects other modes within code blocks 2022-07-07 14:41:46 +01:00
Calvin Rose
e001efa9fd Fix #996 - linking command works on busybox. 2022-07-04 16:48:07 -05:00
Calvin Rose
435e64d4cf Allow shorthand for setting task-id on new threads with flag.
Avoids the need to wrap function bodies in closures in many cases.
2022-07-03 12:08:21 -05:00
Calvin Rose
f296c8f5fb Merge branch 'master' of github.com:janet-lang/janet 2022-07-02 21:11:55 -05:00
Calvin Rose
8d0e6ed32f Fix function handlers for :out and :err.
They were not properly handled for formatting functions.
2022-07-02 21:11:05 -05:00
Calvin Rose
b6a36afffe Merge pull request #994 from shassard/master
Use relative path for include/janet.h symlink
2022-07-02 12:30:39 -05:00
Stephen Hassard
e422abc269 Use relative path for include/janet.h symlink
When using make to build an rpm, the current symlink is
created with an absolute path to the buildroot as causes
the make install target to fail with:

error: Symlink points to BuildRoot: /usr/include/janet.h -> /home/stephen/rpmbuild/BUILDROOT/janet-1.23.0-3.x86_64/usr/include/janet/janet.h

We can create the link relatively which makes this more
portable, where:

ln -sf -t '/home/stephen/rpmbuild/BUILDROOT/janet-1.23.0-3.x86_64/usr/include' janet.h janet/janet.h

Resulting in the following symlink:

ls -la BUILDROOT/usr/include/janet.h
lrwxrwxrwx. 1 stephen stephen 13 Jul  2 08:17 BUILDROOT/usr/include/janet.h -> janet/janet.h

This symlink can then be properly packaged without path
issues.

Signed-off-by: Stephen Hassard <steve@hassard.net>
2022-07-02 08:52:17 -07:00
Calvin Rose
221d71d07b Merge pull request #993 from pepe/test-tabseq
Add basic test for tabseq
2022-07-02 09:30:59 -05:00
Calvin Rose
9f35f0837e Merge pull request #991 from pepe/master
Trace function to the stderr
2022-07-02 09:29:18 -05:00
Josef Pospíšil
515891b035 Add basic test for tabseq 2022-07-02 07:43:28 +02:00
Josef Pospíšil
94a506876f Trace function to the stderr 2022-07-01 12:23:25 +02:00
Calvin Rose
9bde57854a Add tabseq macro. 2022-06-28 22:51:41 -05:00
Calvin Rose
f456369941 Add support for a dyn :task-id
Adds extra information to default information from supervisor
channels. For threaded channels as supervisors, we don't get
the source fiber so identifying the source of messages was not
possible. This change allows better multithreading with  supervisors.
2022-06-25 18:51:21 -05:00
bakpakin
e0b7533c39 Add toggle macro. 2022-05-12 15:36:29 -05:00
27 changed files with 580 additions and 288 deletions

View File

@@ -13,7 +13,7 @@ tasks:
gmake test-install
- meson_min: |
cd janet
meson setup build_meson_min --buildtype=release -Dsingle_threaded=true -Dnanbox=false -Ddynamic_modules=false -Ddocstrings=false -Dnet=false -Dsourcemaps=false -Dpeg=false -Dassembler=false -Dint_types=false -Dtyped_array=false -Dreduced_os=true -Dffi=false
meson setup build_meson_min --buildtype=release -Dsingle_threaded=true -Dnanbox=false -Ddynamic_modules=false -Ddocstrings=false -Dnet=false -Dsourcemaps=false -Dpeg=false -Dassembler=false -Dint_types=false -Dreduced_os=true -Dffi=false
cd build_meson_min
ninja
- meson_prf: |

View File

@@ -1,6 +1,21 @@
# Changelog
All notable changes to this project will be documented in this file.
## 1.24.1 - 2022-08-24
- Fix FFI bug on Linux/Posix
- Improve parse error messages for bad delimiters.
- Add optional `name` parameter to the `short-fn` macro.
## 1.24.0 - 2022-08-14
- Add FFI support to 64-bit windows compiled with MSVC
- Don't process shared object names passed to dlopen.
- Add better support for windows console in the default shell.c for auto-completion and
other shell-like input features.
- Improve default error message from `assert`.
- Add the `tabseq` macro for simpler table comprehensions.
- Allow setting `(dyn :task-id)` in fibers to improve context in supervisor messages. Prior to
this change, supervisor messages over threaded channels would be from ambiguous threads/fibers.
## 1.23.0 - 2022-06-20
- Add experimental `ffi/` module for interfacing with dynamic libraries and raw function pointers. Only available
on 64 bit linux, mac, and bsd systems.

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2021 Calvin Rose
# Copyright (c) 2022 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
@@ -168,9 +168,9 @@ build/c/janet.c: build/janet_boot src/boot/boot.janet
########################
ifeq ($(UNAME), Darwin)
SONAME=libjanet.1.23.dylib
SONAME=libjanet.1.24.dylib
else
SONAME=libjanet.so.1.23
SONAME=libjanet.so.1.24
endif
build/c/shell.c: src/mainclient/shell.c
@@ -283,7 +283,7 @@ install: $(JANET_TARGET) $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/janet.pc
cp $(JANET_TARGET) '$(DESTDIR)$(BINDIR)/janet'
mkdir -p '$(DESTDIR)$(INCLUDEDIR)/janet'
cp -r build/janet.h '$(DESTDIR)$(INCLUDEDIR)/janet'
ln -sf '$(DESTDIR)$(INCLUDEDIR)/janet/janet.h' '$(DESTDIR)$(INCLUDEDIR)/janet.h'
ln -sf -T ./janet/janet.h '$(DESTDIR)$(INCLUDEDIR)/janet.h' || true #fixme bsd
mkdir -p '$(DESTDIR)$(JANET_PATH)'
mkdir -p '$(DESTDIR)$(LIBDIR)'
if test $(UNAME) = Darwin ; then \
@@ -300,7 +300,7 @@ install: $(JANET_TARGET) $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/janet.pc
cp janet.1 '$(DESTDIR)$(JANET_MANPATH)'
mkdir -p '$(DESTDIR)$(JANET_PKG_CONFIG_PATH)'
cp build/janet.pc '$(DESTDIR)$(JANET_PKG_CONFIG_PATH)/janet.pc'
[ -z '$(DESTDIR)' ] && $(LDCONFIG) || true
[ -z '$(DESTDIR)' ] && $(LDCONFIG) || echo "You can ignore this error for non-Linux systems or local installs"
install-jpm-git: $(JANET_TARGET)
mkdir -p build

View File

@@ -2,22 +2,33 @@
#include <stdint.h>
#include <string.h>
#ifdef _WIN32
#define EXPORTER __declspec(dllexport)
#else
#define EXPORTER
#endif
EXPORTER
int int_fn(int a, int b) {
return (a << 2) + b;
}
EXPORTER
double my_fn(int64_t a, int64_t b, const char *x) {
return (double)(a + b) + 0.5 + strlen(x);
}
EXPORTER
double double_fn(double x, double y, double z) {
return (x + y) * z * 3;
}
EXPORTER
double double_many(double x, double y, double z, double w, double a, double b) {
return x + y + z + w + a + b;
}
EXPORTER
double double_lots(
double a,
double b,
@@ -32,6 +43,32 @@ double double_lots(
return i + j;
}
EXPORTER
double double_lots_2(
double a,
double b,
double c,
double d,
double e,
double f,
double g,
double h,
double i,
double j) {
return a +
10.0 * b +
100.0 * c +
1000.0 * d +
10000.0 * e +
100000.0 * f +
1000000.0 * g +
10000000.0 * h +
100000000.0 * i +
1000000000.0 * j;
}
EXPORTER
double float_fn(float x, float y, float z) {
return (x + y) * z;
}
@@ -47,16 +84,19 @@ typedef struct {
int c;
} intintint;
EXPORTER
int intint_fn(double x, intint ii) {
printf("double: %g\n", x);
return ii.a + ii.b;
}
EXPORTER
int intintint_fn(double x, intintint iii) {
printf("double: %g\n", x);
return iii.a + iii.b + iii.c;
}
EXPORTER
intint return_struct(int i) {
intint ret;
ret.a = i;
@@ -70,6 +110,7 @@ typedef struct {
int64_t c;
} big;
EXPORTER
big struct_big(int i, double d) {
big ret;
ret.a = i;
@@ -78,10 +119,12 @@ big struct_big(int i, double d) {
return ret;
}
EXPORTER
void void_fn(void) {
printf("void fn ran\n");
}
EXPORTER
void void_ret_fn(int x) {
printf("void fn ran: %d\n", x);
}

View File

@@ -2,96 +2,47 @@
# Simple FFI test script that tests against a simple shared object
#
(def ffi/loc "examples/ffi/so.so")
(def is-windows (= :windows (os/which)))
(def ffi/loc (string "examples/ffi/so." (if is-windows "dll" "so")))
(def ffi/source-loc "examples/ffi/so.c")
(os/execute ["cc" ffi/source-loc "-shared" "-o" ffi/loc] :px)
(def module (ffi/native ffi/loc))
(if is-windows
(os/execute ["cl.exe" "/nologo" "/LD" ffi/source-loc "/link" "/DLL" (string "/OUT:" ffi/loc)] :px)
(os/execute ["cc" ffi/source-loc "-shared" "-o" ffi/loc] :px))
(def int-fn-sig (ffi/signature :default :int :int :int))
(def int-fn-pointer (ffi/lookup module "int_fn"))
(defn int-fn
[x y]
(ffi/call int-fn-pointer int-fn-sig x y))
(def double-fn-sig (ffi/signature :default :double :double :double :double))
(def double-fn-pointer (ffi/lookup module "double_fn"))
(defn double-fn
[x y z]
(ffi/call double-fn-pointer double-fn-sig x y z))
(def double-many-sig (ffi/signature :default :double :double :double :double :double :double :double))
(def double-many-pointer (ffi/lookup module "double_many"))
(defn double-many
[x y z w a b]
(ffi/call double-many-pointer double-many-sig x y z w a b))
(def double-lots-sig (ffi/signature :default :double
:double :double :double :double :double
:double :double :double :double :double))
(def double-lots-pointer (ffi/lookup module "double_lots"))
(defn double-lots
[a b c d e f g h i j]
(ffi/call double-lots-pointer double-lots-sig a b c d e f g h i j))
(def float-fn-sig (ffi/signature :default :double :float :float :float))
(def float-fn-pointer (ffi/lookup module "float_fn"))
(defn float-fn
[x y z]
(ffi/call float-fn-pointer float-fn-sig x y z))
(def intint-fn-sig (ffi/signature :default :int :double [:int :int]))
(def intint-fn-pointer (ffi/lookup module "intint_fn"))
(defn intint-fn
[x ii]
(ffi/call intint-fn-pointer intint-fn-sig x ii))
(def return-struct-sig (ffi/signature :default [:int :int] :int))
(def return-struct-pointer (ffi/lookup module "return_struct"))
(defn return-struct-fn
[i]
(ffi/call return-struct-pointer return-struct-sig i))
(ffi/context ffi/loc)
(def intintint (ffi/struct :int :int :int))
(def intintint-fn-sig (ffi/signature :default :int :double intintint))
(def intintint-fn-pointer (ffi/lookup module "intintint_fn"))
(defn intintint-fn
[x iii]
(ffi/call intintint-fn-pointer intintint-fn-sig x iii))
(def big (ffi/struct :s64 :s64 :s64))
(def struct-big-fn-sig (ffi/signature :default big :int :double))
(def struct-big-fn-pointer (ffi/lookup module "struct_big"))
(defn struct-big-fn
[i d]
(ffi/call struct-big-fn-pointer struct-big-fn-sig i d))
(def void-fn-pointer (ffi/lookup module "void_fn"))
(def void-fn-sig (ffi/signature :default :void))
(defn void-fn
[]
(ffi/call void-fn-pointer void-fn-sig))
#
# Call functions
#
(pp (void-fn))
(pp (int-fn 10 20))
(pp (double-fn 1.5 2.5 3.5))
(pp (double-many 1 2 3 4 5 6))
(pp (double-lots 1 2 3 4 5 6 7 8 9 10))
(pp (float-fn 8 4 17))
(pp (intint-fn 123.456 [10 20]))
(pp (intintint-fn 123.456 [10 20 30]))
(pp (return-struct-fn 42))
(pp (struct-big-fn 11 99.5))
(assert (= 60 (int-fn 10 20)))
(assert (= 42 (double-fn 1.5 2.5 3.5)))
(assert (= 21 (double-many 1 2 3 4 5 6)))
(assert (= 19 (double-lots 1 2 3 4 5 6 7 8 9 10)))
(assert (= 204 (float-fn 8 4 17)))
(ffi/defbind int-fn :int [a :int b :int])
(ffi/defbind double-fn :double [a :double b :double c :double])
(ffi/defbind double-many :double
[x :double y :double z :double w :double a :double b :double])
(ffi/defbind double-lots :double
[a :double b :double c :double d :double e :double f :double g :double h :double i :double j :double])
(ffi/defbind float-fn :double
[x :float y :float z :float])
(ffi/defbind intint-fn :int
[x :double ii [:int :int]])
(ffi/defbind return-struct [:int :int]
[i :int])
(ffi/defbind intintint-fn :int
[x :double iii intintint])
(ffi/defbind struct-big big
[i :int d :double])
(ffi/defbind void-fn :void [])
(ffi/defbind double-lots-2 :double
[a :double
b :double
c :double
d :double
e :double
f :double
g :double
h :double
i :double
j :double])
#
# Struct reading and writing
@@ -129,4 +80,30 @@
(check-round-trip s [1 3 5 123.5])
(check-round-trip s [-1 -3 -5 -123.5])
#
# Call functions
#
(tracev (double-many 1 2 3 4 5 6))
(tracev (string/format "%.17g" (double-many 1 2 3 4 5 6)))
(tracev (type (double-many 1 2 3 4 5 6)))
(tracev (double-lots-2 0 1 2 3 4 5 6 7 8 9))
(tracev (void-fn))
(tracev (int-fn 10 20))
(tracev (double-fn 1.5 2.5 3.5))
(tracev (double-lots 1 2 3 4 5 6 7 8 9 10))
(tracev (float-fn 8 4 17))
(tracev (intint-fn 123.456 [10 20]))
(tracev (intintint-fn 123.456 [10 20 30]))
(tracev (return-struct 42))
(tracev (double-lots 1 2 3 4 5 6 700 800 9 10))
(tracev (struct-big 11 99.5))
(assert (= 9876543210 (double-lots-2 0 1 2 3 4 5 6 7 8 9)))
(assert (= 60 (int-fn 10 20)))
(assert (= 42 (double-fn 1.5 2.5 3.5)))
(assert (= 21 (math/round (double-many 1 2 3 4 5 6.01))))
(assert (= 19 (double-lots 1 2 3 4 5 6 7 8 9 10)))
(assert (= 204 (float-fn 8 4 17)))
(print "Done.")

7
examples/ffi/win32.janet Normal file
View File

@@ -0,0 +1,7 @@
(ffi/context "user32.dll")
(ffi/defbind MessageBoxA :int
[w :ptr text :string cap :string typ :int])
(MessageBoxA nil "Hello, World!" "Test" 0)

View File

@@ -164,10 +164,15 @@ Execute a string of Janet source. Source code is executed in the order it is enc
arguments are executed before later ones.
.TP
.BR \-E\ code arguments
.BR \-E\ code\ arguments...
Execute a single Janet expression as a Janet short-fn, passing the remaining command line arguments to the expression. This allows
more concise one-liners with command line arguments.
Example: janet -E '(print $0)' 12 is equivalent to '((short-fn (print $0)) 12)', which is in turn equivalent to
`((fn [k] (print k)) 12)`
See docs for the `short-fn` function for more details.
.TP
.BR \-d
Enable debug mode. On all terminating signals as well the debug signal, this will

View File

@@ -20,7 +20,7 @@
project('janet', 'c',
default_options : ['c_std=c99', 'build.c_std=c99', 'b_lundef=false', 'default_library=both'],
version : '1.23.0')
version : '1.24.1')
# Global settings
janet_path = join_paths(get_option('prefix'), get_option('libdir'), 'janet')

View File

@@ -76,6 +76,11 @@
[name & more]
~(var ,name :private ,;more))
(defmacro toggle
"Set a value to its boolean inverse. Same as `(set value (not value))`."
[value]
~(set ,value (,not ,value)))
(defn defglobal
"Dynamically create a global def."
[name value]
@@ -158,7 +163,7 @@
(def ,v ,x)
(if ,v
,v
(,error ,(if err err "assert failure")))))
(,error ,(if err err (string/format "assert failure in %j" x))))))
(defn errorf
"A combination of `error` and `string/format`. Equivalent to `(error (string/format fmt ;args))`."
@@ -606,13 +611,20 @@
See `loop` for details.``
[head & body]
(def $accum (gensym))
~(do (def ,$accum @[]) (loop ,head (array/push ,$accum (do ,;body))) ,$accum))
~(do (def ,$accum @[]) (loop ,head (,array/push ,$accum (do ,;body))) ,$accum))
(defmacro tabseq
``Similar to `loop`, but accumulates key value pairs into a table.
See `loop` for details.``
[head key-body & value-body]
(def $accum (gensym))
~(do (def ,$accum @{}) (loop ,head (,put ,$accum ,key-body (do ,;value-body))) ,$accum))
(defmacro generate
``Create a generator expression using the `loop` syntax. Returns a fiber
that yields all values inside the loop in order. See `loop` for details.``
[head & body]
~(fiber/new (fn [] (loop ,head (yield (do ,;body)))) :yi))
~(,fiber/new (fn [] (loop ,head (yield (do ,;body)))) :yi))
(defmacro coro
"A wrapper for making fibers that may yield multiple values (coroutine). Same as `(fiber/new (fn [] ;body) :yi)`."
@@ -2165,7 +2177,7 @@
|(+ $ $) # use pipe reader macro for terse function literals.
|(+ $&) # variadic functions
```
[arg]
[arg &opt name]
(var max-param-seen -1)
(var vararg false)
(defn saw-special-arg
@@ -2191,8 +2203,9 @@
x))
x))
(def expanded (macex arg on-binding))
(def name-splice (if name [name] []))
(def fn-args (seq [i :range [0 (+ 1 max-param-seen)]] (symbol '$ i)))
~(fn [,;fn-args ,;(if vararg ['& '$&] [])] ,expanded))
~(fn ,;name-splice [,;fn-args ,;(if vararg ['& '$&] [])] ,expanded))
###
###
@@ -2768,13 +2781,13 @@
(def c ((:where p) 0))
(def prpt (string "debug[" level "]:" c ":" status "> "))
(getline prpt buf nextenv))
(print "entering debug[" level "] - (quit) to exit")
(eprint "entering debug[" level "] - (quit) to exit")
(flush)
(run-context
{:chunks debugger-chunks
:on-status (debugger-on-status-var nextenv (+ 1 level) true)
:env nextenv})
(print "exiting debug[" level "]")
(eprint "exiting debug[" level "]")
(flush)
(nextenv :resume-value))
@@ -3013,7 +3026,7 @@
:italics ["*" "*"]
:bold ["**" "**"]}))
(def modes @{})
(defn toggle [mode]
(defn toggle-mode [mode]
(def active (get modes mode))
(def delims (get delimiters mode))
(put modes mode (not active))
@@ -3123,7 +3136,7 @@
(def token @"")
(var token-length 0)
(defn delim [mode]
(def d (toggle mode))
(def d (toggle-mode mode))
(if-not has-color (+= token-length (length d)))
(buffer/push token d))
(defn endtoken []
@@ -3134,16 +3147,18 @@
(def b (get line i))
(cond
(or (= b (chr "\n")) (= b (chr " "))) (endtoken)
(= b (chr `\`)) (do
(++ token-length)
(buffer/push token (get line (++ i))))
(= b (chr "_")) (delim :underline)
(= b (chr "`")) (delim :code)
(= b (chr "*"))
(if (= (chr "*") (get line (+ i 1)))
(do (++ i)
(delim :bold))
(delim :italics))
(not (modes :code)) (cond
(= b (chr `\`)) (do
(++ token-length)
(buffer/push token (get line (++ i))))
(= b (chr "_")) (delim :underline)
(= b (chr "*"))
(if (= (chr "*") (get line (+ i 1)))
(do (++ i)
(delim :bold))
(delim :italics))
(do (++ token-length) (buffer/push token b)))
(do (++ token-length) (buffer/push token b))))
(endtoken)
(tuple/slice tokens))
@@ -3657,6 +3672,7 @@
(defmacro ffi/defbind
"Generate bindings for native functions in a convenient manner."
[name ret-type & body]
(def real-ret-type (eval ret-type))
(def meta (slice body 0 -2))
(def arg-pairs (partition 2 (last body)))
(def formal-args (map 0 arg-pairs))
@@ -3668,9 +3684,9 @@
:map-symbols ms} (assert (dyn *ffi-context*) "no ffi context found"))
(def raw-symbol (ms name))
(defn make-sig []
(ffi/signature :default ret-type ;computed-type-args))
(ffi/signature :default real-ret-type ;computed-type-args))
(defn make-ptr []
(assert (ffi/lookup (if lazy (llib) lib) raw-symbol) "failed to find symbol"))
(assert (ffi/lookup (if lazy (llib) lib) raw-symbol) (string "failed to find ffi symbol " raw-symbol)))
(if lazy
~(defn ,name ,;meta [,;formal-args]
(,ffi/call (,(delay (make-ptr))) (,(delay (make-sig))) ,;formal-args))
@@ -3871,7 +3887,7 @@
"E" (fn E-switch [i &]
(set no-file false)
(def subargs (array/slice args (+ i 2)))
(def src ~|,(parse (in args (+ i 1))))
(def src ~(short-fn ,(parse (in args (+ i 1))) E-expression))
(def thunk (compile src))
(if (function? thunk)
((thunk) ;subargs)

View File

@@ -4,10 +4,10 @@
#define JANETCONF_H
#define JANET_VERSION_MAJOR 1
#define JANET_VERSION_MINOR 23
#define JANET_VERSION_PATCH 0
#define JANET_VERSION_MINOR 24
#define JANET_VERSION_PATCH 1
#define JANET_VERSION_EXTRA ""
#define JANET_VERSION "1.23.0"
#define JANET_VERSION "1.24.1"
/* #define JANET_BUILD "local" */

View File

@@ -996,7 +996,7 @@ JanetCompileResult janet_compile(Janet source, JanetTable *env, const uint8_t *w
}
/* C Function for compiling */
JANET_CORE_FN(cfun,
JANET_CORE_FN(cfun_compile,
"(compile ast &opt env source lints)",
"Compiles an Abstract Syntax Tree (ast) into a function. "
"Pair the compile function with parsing functionality to implement "
@@ -1043,7 +1043,7 @@ JANET_CORE_FN(cfun,
void janet_lib_compile(JanetTable *env) {
JanetRegExt cfuns[] = {
JANET_CORE_REG("compile", cfun),
JANET_CORE_REG("compile", cfun_compile),
JANET_REG_END
};
janet_core_cfuns_ext(env, NULL, cfuns);

View File

@@ -535,10 +535,15 @@ static int janet_channel_push(JanetChannel *channel, Janet x, int mode);
static int janet_channel_pop(JanetChannel *channel, Janet *item, int is_choice);
static Janet make_supervisor_event(const char *name, JanetFiber *fiber, int threaded) {
Janet tup[2];
Janet tup[3];
tup[0] = janet_ckeywordv(name);
tup[1] = threaded ? fiber->last_value : janet_wrap_fiber(fiber) ;
return janet_wrap_tuple(janet_tuple_n(tup, 2));
if (fiber->env != NULL) {
tup[2] = janet_table_get(fiber->env, janet_ckeywordv("task-id"));
} else {
tup[2] = janet_wrap_nil();
}
return janet_wrap_tuple(janet_tuple_n(tup, 3));
}
/* Common init code */
@@ -2719,6 +2724,8 @@ JANET_CORE_FN(cfun_ev_go,
return janet_wrap_fiber(fiber);
}
#define JANET_THREAD_SUPERVISOR_FLAG 0x100
/* For ev/thread - Run an interpreter in the new thread. */
static JanetEVGenericMessage janet_go_thread_subr(JanetEVGenericMessage args) {
JanetBuffer *buffer = (JanetBuffer *) args.argp;
@@ -2741,7 +2748,7 @@ static JanetEVGenericMessage janet_go_thread_subr(JanetEVGenericMessage args) {
}
/* Get supervsior */
if (flags & 0x8) {
if (flags & JANET_THREAD_SUPERVISOR_FLAG) {
Janet sup =
janet_unmarshal(nextbytes, endbytes - nextbytes,
JANET_MARSHAL_UNSAFE, NULL, &nextbytes);
@@ -2793,6 +2800,10 @@ static JanetEVGenericMessage janet_go_thread_subr(JanetEVGenericMessage args) {
} else {
fiber = janet_unwrap_fiber(fiberv);
}
if (flags & 0x8) {
if (NULL == fiber->env) fiber->env = janet_table(0);
janet_table_put(fiber->env, janet_ckeywordv("task-id"), value);
}
fiber->supervisor_channel = janet_vm.user;
janet_schedule(fiber, value);
janet_loop();
@@ -2837,6 +2848,7 @@ JANET_CORE_FN(cfun_ev_thread,
"If you want to run the thread without waiting for a result, pass the `:n` flag to return nil immediately. "
"Otherwise, returns nil. Available flags:\n\n"
"* `:n` - return immediately\n"
"* `:t` - set the task-id of the new thread to value. The task-id is passed in messages to the supervisor channel.\n"
"* `:a` - don't copy abstract registry to new thread (performance optimization)\n"
"* `:c` - don't copy cfunction registry to new thread (performance optimization)") {
janet_arity(argc, 1, 4);
@@ -2844,10 +2856,10 @@ JANET_CORE_FN(cfun_ev_thread,
if (!janet_checktype(argv[0], JANET_FUNCTION)) janet_getfiber(argv, 0);
uint64_t flags = 0;
if (argc >= 3) {
flags = janet_getflags(argv, 2, "nac");
flags = janet_getflags(argv, 2, "nact");
}
void *supervisor = janet_optabstract(argv, argc, 3, &janet_channel_type, janet_vm.root_fiber->supervisor_channel);
if (NULL != supervisor) flags |= 0x8;
if (NULL != supervisor) flags |= JANET_THREAD_SUPERVISOR_FLAG;
/* Marshal arguments for the new thread. */
JanetBuffer *buffer = janet_malloc(sizeof(JanetBuffer));
@@ -2858,7 +2870,7 @@ JANET_CORE_FN(cfun_ev_thread,
if (!(flags & 0x2)) {
janet_marshal(buffer, janet_wrap_table(janet_vm.abstract_registry), NULL, JANET_MARSHAL_UNSAFE);
}
if (flags & 0x8) {
if (flags & JANET_THREAD_SUPERVISOR_FLAG) {
janet_marshal(buffer, janet_wrap_abstract(supervisor), NULL, JANET_MARSHAL_UNSAFE);
}
if (!(flags & 0x4)) {

View File

@@ -355,11 +355,11 @@ static JanetFFIStruct *build_struct_type(int32_t argc, const Janet *argv) {
if (all_packed || pack_one) {
if (st->size % el_align != 0) is_aligned = 0;
st->fields[i].offset = st->size;
st->size += el_size;
st->size += (uint32_t) el_size;
} else {
if (el_align > st->align) st->align = el_align;
st->fields[i].offset = (((st->size + el_align - 1) / el_align) * el_align);
st->size = el_size + st->fields[i].offset;
if (el_align > st->align) st->align = (uint32_t) el_align;
st->fields[i].offset = (uint32_t)(((st->size + el_align - 1) / el_align) * el_align);
st->size = (uint32_t)(el_size + st->fields[i].offset);
}
i++;
}
@@ -477,7 +477,7 @@ static void janet_ffi_write_one(void *to, const Janet *argv, int32_t n, JanetFFI
}
for (int32_t i = 0; i < els.len; i++) {
JanetFFIType tp = st->fields[i].type;
janet_ffi_write_one(to + st->fields[i].offset, els.items, i, tp, recur - 1);
janet_ffi_write_one((char *) to + st->fields[i].offset, els.items, i, tp, recur - 1);
}
}
break;
@@ -485,7 +485,7 @@ static void janet_ffi_write_one(void *to, const Janet *argv, int32_t n, JanetFFI
((double *)(to))[0] = janet_getnumber(argv, n);
break;
case JANET_FFI_TYPE_FLOAT:
((float *)(to))[0] = janet_getnumber(argv, n);
((float *)(to))[0] = (float) janet_getnumber(argv, n);
break;
case JANET_FFI_TYPE_PTR:
((void **)(to))[0] = janet_ffi_getpointer(argv, n);
@@ -509,13 +509,13 @@ static void janet_ffi_write_one(void *to, const Janet *argv, int32_t n, JanetFFI
((int64_t *)(to))[0] = janet_getinteger64(argv, n);
break;
case JANET_FFI_TYPE_UINT8:
((uint8_t *)(to))[0] = janet_getuinteger64(argv, n);
((uint8_t *)(to))[0] = (uint8_t) janet_getuinteger64(argv, n);
break;
case JANET_FFI_TYPE_UINT16:
((uint16_t *)(to))[0] = janet_getuinteger64(argv, n);
((uint16_t *)(to))[0] = (uint16_t) janet_getuinteger64(argv, n);
break;
case JANET_FFI_TYPE_UINT32:
((uint32_t *)(to))[0] = janet_getuinteger64(argv, n);
((uint32_t *)(to))[0] = (uint32_t) janet_getuinteger64(argv, n);
break;
case JANET_FFI_TYPE_UINT64:
((uint64_t *)(to))[0] = janet_getuinteger64(argv, n);
@@ -684,7 +684,7 @@ JANET_CORE_FN(cfun_ffi_signature,
#ifdef JANET_FFI_WIN64_ENABLED
case JANET_FFI_CC_WIN_64: {
size_t ret_size = type_size(ret.type);
size_t ref_stack_count = 0;
uint32_t ref_stack_count = 0;
ret.spec = JANET_WIN64_REGISTER;
uint32_t next_register = 0;
if (ret_size != 1 && ret_size != 2 && ret_size != 4 && ret_size != 8) {
@@ -699,20 +699,19 @@ JANET_CORE_FN(cfun_ffi_signature,
size_t el_size = type_size(mappings[i].type);
int is_register_sized = (el_size == 1 || el_size == 2 || el_size == 4 || el_size == 8);
if (next_register < 4) {
mappings[i].offset = next_register++;
mappings[i].offset = next_register;
if (is_register_sized) {
mappings[i].spec = JANET_WIN64_REGISTER;
/* Select variant based on position of floating point arguments */
if (mappings[i].type.prim == JANET_FFI_TYPE_FLOAT ||
mappings[i].type.prim == JANET_FFI_TYPE_DOUBLE) {
variant += 1 << next_register;
variant += 1 << (3 - next_register);
}
} else {
mappings[i].spec = JANET_WIN64_REGISTER_REF;
mappings[i].offset2 = ref_stack_count;
ref_stack_count += (el_size + 15) / 16;
ref_stack_count += (uint32_t)((el_size + 15) / 16);
}
next_register++;
} else {
if (is_register_sized) {
mappings[i].spec = JANET_WIN64_STACK;
@@ -723,30 +722,25 @@ JANET_CORE_FN(cfun_ffi_signature,
mappings[i].offset = stack_count;
stack_count++;
mappings[i].offset2 = ref_stack_count;
ref_stack_count += (el_size + 15) / 16;
ref_stack_count += (uint32_t)((el_size + 15) / 16);
}
}
}
/* Take into account reference arguments and align to 16 bytes just in case */
/* Add reference items */
size_t old_stack_count = stack_count;
stack_count += 2 * ref_stack_count;
if (stack_count & 1) {
if (stack_count & 0x1) {
stack_count++;
}
/* Invert stack
* Offsets are in units of 8-bytes */
for (uint32_t i = 0; i < arg_count; i++) {
uint32_t old_offset = mappings[i].offset;
if (mappings[i].spec == JANET_WIN64_STACK) {
mappings[i].offset = stack_count - 1 - old_offset;
} else if (mappings[i].spec == JANET_WIN64_STACK_REF) {
mappings[i].offset = stack_count - 1 - old_offset;
}
if (mappings[i].spec == JANET_WIN64_STACK_REF || mappings[i].spec == JANET_WIN64_REGISTER_REF) {
/* Align size to 16 bytes */
size_t size = (type_size(mappings[i].type) + 15) & ~0xFUL;
mappings[i].offset2 = stack_count - mappings[i].offset2 - (size / 8);
mappings[i].offset2 = (uint32_t)(stack_count - mappings[i].offset2 - (size / 8));
}
}
@@ -805,15 +799,6 @@ JANET_CORE_FN(cfun_ffi_signature,
}
}
}
/* Invert stack */
for (uint32_t i = 0; i < arg_count; i++) {
if (mappings[i].spec == JANET_SYSV64_MEMORY) {
uint32_t old_offset = mappings[i].offset;
size_t el_size = type_size(mappings[i].type);
mappings[i].offset = stack_count - ((el_size + 7) / 8) - old_offset;
}
}
}
break;
#endif
@@ -961,33 +946,33 @@ static Janet janet_ffi_win64(JanetFFISignature *signature, void *function_pointe
} ret_reg;
JanetFFIWordSpec ret_spec = signature->ret.spec;
void *ret_mem = &ret_reg.integer;
if (ret_spec == JANET_WIN64_STACK) {
if (ret_spec == JANET_WIN64_REGISTER_REF) {
ret_mem = alloca(type_size(signature->ret.type));
regs[0].integer = (uint64_t) ret_mem;
}
uint64_t *stack = alloca(signature->stack_count * 8);
stack -= 2; /* hack to get proper stack placement */
for (uint32_t i = 0; i < signature->arg_count; i++) {
int32_t n = i + 2;
JanetFFIMapping arg = signature->args[i];
if (arg.spec == JANET_WIN64_STACK) {
janet_ffi_write_one(stack + arg.offset, argv, n, arg.type, JANET_FFI_MAX_RECUR);
} else if (arg.spec == JANET_WIN64_STACK_REF) {
uint8_t *ptr = (uint8_t *)(stack + args.offset2);
uint8_t *ptr = (uint8_t *)(stack + arg.offset2);
janet_ffi_write_one(ptr, argv, n, arg.type, JANET_FFI_MAX_RECUR);
stack[args.offset] = (uint64_t) ptr;
stack[arg.offset] = (uint64_t) ptr;
} else if (arg.spec == JANET_WIN64_REGISTER_REF) {
uint8_t *ptr = (uint8_t *)(stack + args.offset2);
uint8_t *ptr = (uint8_t *)(stack + arg.offset2);
janet_ffi_write_one(ptr, argv, n, arg.type, JANET_FFI_MAX_RECUR);
regs[args.offset].integer = (uint64_t) ptr;
regs[arg.offset].integer = (uint64_t) ptr;
} else {
janet_ffi_write_one((uint8_t *) &regs[arg.offset].integer, argv, n, arg.type, JANET_FFI_MAX_RECUR);
}
}
/* the seasoned programmer who cut their teeth on assembly is probably quietly shaking their head by now... */
switch (signature->variant) {
default:
janet_panic("unknown variant");
janet_panicf("unknown variant %d", signature->variant);
case 0:
ret_reg.integer = ((win64_variant_i_iiii *) function_pointer)(regs[0].integer, regs[1].integer, regs[2].integer, regs[3].integer);
break;
@@ -1121,7 +1106,7 @@ JANET_CORE_FN(cfun_ffi_buffer_write,
"or to files. Returns a modifed buffer or a new buffer if one is not supplied.") {
janet_arity(argc, 2, 3);
JanetFFIType type = decode_ffi_type(argv[0]);
size_t el_size = type_size(type);
uint32_t el_size = (uint32_t) type_size(type);
JanetBuffer *buffer = janet_optbuffer(argv, argc, 2, el_size);
janet_buffer_extra(buffer, el_size);
memset(buffer->data, 0, el_size);
@@ -1183,9 +1168,7 @@ JANET_CORE_FN(janet_core_raw_native,
"Returns a `core/native`.") {
janet_arity(argc, 0, 1);
const char *path = janet_optcstring(argv, argc, 0, NULL);
char *processed_name = (NULL == path) ? NULL : get_processed_name(path);
Clib lib = load_clib(processed_name);
if (NULL != path && path != processed_name) janet_free(processed_name);
Clib lib = load_clib(path);
if (!lib) janet_panic(error_clib());
JanetAbstractNative *anative = janet_abstract(&janet_native_type, sizeof(JanetAbstractNative));
anative->clib = lib;

View File

@@ -545,6 +545,16 @@ static Janet cfun_io_printf_impl_x(int32_t argc, Janet *argv, int newline,
if (newline) janet_buffer_push_u8(buf, '\n');
return janet_wrap_nil();
}
case JANET_FUNCTION: {
/* Special case function */
JanetFunction *fun = janet_unwrap_function(x);
JanetBuffer *buf = janet_buffer(0);
janet_buffer_format(buf, fmt, offset, argc, argv);
if (newline) janet_buffer_push_u8(buf, '\n');
Janet args[1] = { janet_wrap_buffer(buf) };
janet_call(fun, 1, args);
return janet_wrap_nil();
}
case JANET_NIL:
f = dflt_file;
if (f == NULL) janet_panic("cannot print to nil");
@@ -684,6 +694,16 @@ void janet_dynprintf(const char *name, FILE *dflt_file, const char *format, ...)
janet_buffer_deinit(&buffer);
break;
}
case JANET_FUNCTION: {
JanetFunction *fun = janet_unwrap_function(x);
int32_t len = 0;
while (format[len]) len++;
JanetBuffer *buf = janet_buffer(len);
janet_formatbv(buf, format, args);
Janet args[1] = { janet_wrap_buffer(buf) };
janet_call(fun, 1, args);
break;
}
case JANET_BUFFER:
janet_formatbv(janet_unwrap_buffer(x), format, args);
break;

View File

@@ -1121,8 +1121,8 @@ JANET_CORE_FN(os_spawn,
"Execute a program on the system and return a handle to the process. Otherwise, takes the "
"same arguments as `os/execute`. Does not wait for the process. "
"For each of the :in, :out, and :err keys to the `env` argument, one "
"can also pass in the keyword `:pipe`"
"to get streams for standard IO of the subprocess that can be read from and written to."
"can also pass in the keyword `:pipe` "
"to get streams for standard IO of the subprocess that can be read from and written to. "
"The returned value `proc` has the fields :in, :out, :err, :return-code, and "
"the additional field :pid on unix-like platforms. Use `(os/proc-wait proc)` to rejoin the "
"subprocess or `(os/proc-kill proc)`.") {
@@ -2036,23 +2036,23 @@ JANET_CORE_FN(os_open,
"Allowed flags are as follows:\n\n"
" * :r - open this file for reading\n"
" * :w - open this file for writing\n"
" * :c - create a new file (O_CREATE)\n"
" * :e - fail if the file exists (O_EXCL)\n"
" * :t - shorten an existing file to length 0 (O_TRUNC)\n\n"
" * :c - create a new file (O\\_CREATE)\n"
" * :e - fail if the file exists (O\\_EXCL)\n"
" * :t - shorten an existing file to length 0 (O\\_TRUNC)\n\n"
"Posix-only flags:\n\n"
" * :a - append to a file (O_APPEND)\n"
" * :x - O_SYNC\n"
" * :C - O_NOCTTY\n\n"
" * :a - append to a file (O\\_APPEND)\n"
" * :x - O\\_SYNC\n"
" * :C - O\\_NOCTTY\n\n"
"Windows-only flags:\n\n"
" * :R - share reads (FILE_SHARE_READ)\n"
" * :W - share writes (FILE_SHARE_WRITE)\n"
" * :D - share deletes (FILE_SHARE_DELETE)\n"
" * :H - FILE_ATTRIBUTE_HIDDEN\n"
" * :O - FILE_ATTRIBUTE_READONLY\n"
" * :F - FILE_ATTRIBUTE_OFFLINE\n"
" * :T - FILE_ATTRIBUTE_TEMPORARY\n"
" * :d - FILE_FLAG_DELETE_ON_CLOSE\n"
" * :b - FILE_FLAG_NO_BUFFERING\n") {
" * :R - share reads (FILE\\_SHARE\\_READ)\n"
" * :W - share writes (FILE\\_SHARE\\_WRITE)\n"
" * :D - share deletes (FILE\\_SHARE\\_DELETE)\n"
" * :H - FILE\\_ATTRIBUTE\\_HIDDEN\n"
" * :O - FILE\\_ATTRIBUTE\\_READONLY\n"
" * :F - FILE\\_ATTRIBUTE\\_OFFLINE\n"
" * :T - FILE\\_ATTRIBUTE\\_TEMPORARY\n"
" * :d - FILE\\_FLAG\\_DELETE\\_ON\\_CLOSE\n"
" * :b - FILE\\_FLAG\\_NO\\_BUFFERING\n") {
janet_arity(argc, 1, 3);
const char *path = janet_getcstring(argv, 0);
const uint8_t *opt_flags = janet_optkeyword(argv, argc, 1, (const uint8_t *) "r");

View File

@@ -206,6 +206,37 @@ static void popstate(JanetParser *p, Janet val) {
}
}
static void delim_error(JanetParser *parser, size_t stack_index, char c, const char *msg) {
JanetParseState *s = parser->states + stack_index;
JanetBuffer *buffer = janet_buffer(40);
if (msg) {
janet_buffer_push_cstring(buffer, msg);
}
if (c) {
janet_buffer_push_u8(buffer, c);
}
if (stack_index > 0) {
janet_buffer_push_cstring(buffer, ", ");
if (s->flags & PFLAG_PARENS) {
janet_buffer_push_u8(buffer, '(');
} else if (s->flags & PFLAG_SQRBRACKETS) {
janet_buffer_push_u8(buffer, '[');
} else if (s->flags & PFLAG_CURLYBRACKETS) {
janet_buffer_push_u8(buffer, '{');
} else if (s->flags & PFLAG_STRING) {
janet_buffer_push_u8(buffer, '"');
} else if (s->flags & PFLAG_LONGSTRING) {
int32_t i;
for (i = 0; i < s->argn; i++) {
janet_buffer_push_u8(buffer, '`');
}
}
janet_formatb(buffer, " opened at line %d, column %d", s->line, s->column);
}
parser->error = (const char *) janet_string(buffer->data, buffer->count);
parser->flag |= JANET_PARSER_GENERATED_ERROR;
}
static int checkescape(uint8_t c) {
switch (c) {
default:
@@ -612,7 +643,7 @@ static int root(JanetParser *p, JanetParseState *state, uint8_t c) {
case '}': {
Janet ds;
if (p->statecount == 1) {
p->error = "unexpected delimiter";
delim_error(p, 0, c, "unexpected closing delimiter ");
return 1;
}
if ((c == ')' && (state->flags & PFLAG_PARENS)) ||
@@ -633,7 +664,7 @@ static int root(JanetParser *p, JanetParseState *state, uint8_t c) {
ds = close_struct(p, state);
}
} else {
p->error = "mismatched delimiter";
delim_error(p, p->statecount - 1, c, "mismatched delimiter ");
return 1;
}
popstate(p, ds);
@@ -684,26 +715,7 @@ void janet_parser_eof(JanetParser *parser) {
size_t oldline = parser->line;
janet_parser_consume(parser, '\n');
if (parser->statecount > 1) {
JanetParseState *s = parser->states + (parser->statecount - 1);
JanetBuffer *buffer = janet_buffer(40);
janet_buffer_push_cstring(buffer, "unexpected end of source, ");
if (s->flags & PFLAG_PARENS) {
janet_buffer_push_u8(buffer, '(');
} else if (s->flags & PFLAG_SQRBRACKETS) {
janet_buffer_push_u8(buffer, '[');
} else if (s->flags & PFLAG_CURLYBRACKETS) {
janet_buffer_push_u8(buffer, '{');
} else if (s->flags & PFLAG_STRING) {
janet_buffer_push_u8(buffer, '"');
} else if (s->flags & PFLAG_LONGSTRING) {
int32_t i;
for (i = 0; i < s->argn; i++) {
janet_buffer_push_u8(buffer, '`');
}
}
janet_formatb(buffer, " opened at line %d, column %d", s->line, s->column);
parser->error = (const char *) janet_string(buffer->data, buffer->count);
parser->flag |= JANET_PARSER_GENERATED_ERROR;
delim_error(parser, parser->statecount - 1, 0, "unexpected end of source");
}
parser->line = oldline;
parser->column = oldcolumn;

View File

@@ -762,8 +762,7 @@ static const char *scanformat(
memset(precision, '\0', 3);
while (*p != '\0' && strchr(FMT_FLAGS, *p) != NULL)
p++; /* skip flags */
if ((size_t)(p - strfrmt) >= sizeof(FMT_FLAGS) / sizeof(char))
janet_panic("invalid format (repeated flags)");
if ((size_t)(p - strfrmt) >= sizeof(FMT_FLAGS)) janet_panic("invalid format (repeated flags)");
if (isdigit((int)(*p)))
width[0] = *p++; /* skip width */
if (isdigit((int)(*p)))

View File

@@ -530,7 +530,7 @@ JANET_CORE_FN(cfun_string_join,
JANET_CORE_FN(cfun_string_format,
"(string/format format & values)",
"Similar to `snprintf`, but specialized for operating with Janet values. Returns "
"Similar to C's `snprintf`, but specialized for operating with Janet values. Returns "
"a new string.") {
janet_arity(argc, 1, -1);
JanetBuffer *buffer = janet_buffer(0);

View File

@@ -36,6 +36,13 @@
#endif
#endif
#ifdef JANET_WINDOWS
#ifdef JANET_DYNAMIC_MODULES
#include <psapi.h>
#pragma comment (lib, "Psapi.lib")
#endif
#endif
#ifdef JANET_APPLE
#include <AvailabilityMacros.h>
#endif
@@ -904,6 +911,7 @@ char *get_processed_name(const char *name) {
}
#if defined(JANET_WINDOWS)
static char error_clib_buf[256];
char *error_clib(void) {
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
@@ -920,6 +928,35 @@ Clib load_clib(const char *name) {
return LoadLibrary(name);
}
}
void free_clib(HINSTANCE clib) {
if (clib != GetModuleHandle(NULL)) {
FreeLibrary(clib);
}
}
void *symbol_clib(HINSTANCE clib, const char *sym) {
if (clib != GetModuleHandle(NULL)) {
return GetProcAddress(clib, sym);
} else {
/* Look up symbols from all loaded modules */
HMODULE hMods[1024];
DWORD needed = 0;
if (EnumProcessModules(GetCurrentProcess(), hMods, sizeof(hMods), &needed)) {
needed /= sizeof(HMODULE);
for (DWORD i = 0; i < needed; i++) {
void *address = GetProcAddress(hMods[i], sym);
if (NULL != address) {
return address;
}
}
} else {
janet_panicf("ffi: %s", error_clib());
}
return NULL;
}
}
#endif
/* Alloc function macro fills */

View File

@@ -140,8 +140,8 @@ typedef int Clib;
#elif defined(JANET_WINDOWS)
#include <windows.h>
typedef HINSTANCE Clib;
#define free_clib(c) FreeLibrary((c))
#define symbol_clib(lib, sym) GetProcAddress((lib), (sym))
void *symbol_clib(Clib clib, const char *sym);
void free_clib(Clib clib);
Clib load_clib(const char *name);
char *error_clib(void);
#else

View File

@@ -348,7 +348,7 @@ int32_t janet_hash(Janet x) {
hash = (int32_t)((hilo << 16) | (hilo >> 16));
} else {
/* Assuming 4 byte pointer (or smaller) */
ptrdiff_t diff = ((char *)janet_unwrap_pointer(x) - (char *)0);
uintptr_t diff = (uintptr_t) janet_unwrap_pointer(x);
uint32_t hilo = (uint32_t) diff * 2654435769u;
hash = (int32_t)((hilo << 16) | (hilo >> 16));
}

View File

@@ -220,14 +220,14 @@
/* Trace a function call */
static void vm_do_trace(JanetFunction *func, int32_t argc, const Janet *argv) {
if (func->def->name) {
janet_printf("trace (%S", func->def->name);
janet_eprintf("trace (%S", func->def->name);
} else {
janet_printf("trace (%p", janet_wrap_function(func));
janet_eprintf("trace (%p", janet_wrap_function(func));
}
for (int32_t i = 0; i < argc; i++) {
janet_printf(" %p", argv[i]);
janet_eprintf(" %p", argv[i]);
}
janet_printf(")\n");
janet_eprintf(")\n");
}
/* Invoke a method once we have looked it up */
@@ -1285,6 +1285,12 @@ JanetSignal janet_step(JanetFiber *fiber, Janet in, Janet *out) {
return signal;
}
static Janet void_cfunction(int32_t argc, Janet *argv) {
(void) argc;
(void) argv;
janet_panic("placeholder");
}
Janet janet_call(JanetFunction *fun, int32_t argc, const Janet *argv) {
/* Check entry conditions */
if (!janet_vm.fiber)
@@ -1292,9 +1298,17 @@ Janet janet_call(JanetFunction *fun, int32_t argc, const Janet *argv) {
if (janet_vm.stackn >= JANET_RECURSION_GUARD)
janet_panic("C stack recursed too deeply");
/* Dirty stack */
int32_t dirty_stack = janet_vm.fiber->stacktop - janet_vm.fiber->stackstart;
if (dirty_stack) {
janet_fiber_cframe(janet_vm.fiber, void_cfunction);
}
/* Tracing */
if (fun->gc.flags & JANET_FUNCFLAG_TRACE) {
janet_vm.stackn++;
vm_do_trace(fun, argc, argv);
janet_vm.stackn--;
}
/* Push frame */
@@ -1322,6 +1336,10 @@ Janet janet_call(JanetFunction *fun, int32_t argc, const Janet *argv) {
/* Teardown */
janet_vm.stackn = oldn;
janet_gcunlock(handle);
if (dirty_stack) {
janet_fiber_popframe(janet_vm.fiber);
janet_vm.fiber->stacktop += dirty_stack;
}
if (signal != JANET_SIGNAL_OK) {
janet_panicv(*janet_vm.return_reg);

View File

@@ -164,9 +164,9 @@ extern "C" {
#endif
/* Enable or disable the FFI library. Currently, FFI only enabled on
* x86-64, non-windows operating systems. */
* x86-64 operating systems. */
#ifndef JANET_NO_FFI
#if !defined(JANET_WINDOWS) && !defined(__EMSCRIPTEN__) && (defined(__x86_64__) || defined(_M_X64))
#if !defined(__EMSCRIPTEN__) && (defined(__x86_64__) || defined(_M_X64))
#define JANET_FFI
#endif
#endif

View File

@@ -87,8 +87,30 @@ static void simpleline(JanetBuffer *buffer) {
}
}
/* Windows */
#if defined(JANET_WINDOWS) || defined(JANET_SIMPLE_GETLINE)
/* State */
#ifndef JANET_SIMPLE_GETLINE
/* static state */
#define JANET_LINE_MAX 1024
#define JANET_MATCH_MAX 256
#define JANET_HISTORY_MAX 100
static JANET_THREAD_LOCAL int gbl_israwmode = 0;
static JANET_THREAD_LOCAL const char *gbl_prompt = "> ";
static JANET_THREAD_LOCAL int gbl_plen = 2;
static JANET_THREAD_LOCAL char gbl_buf[JANET_LINE_MAX];
static JANET_THREAD_LOCAL int gbl_len = 0;
static JANET_THREAD_LOCAL int gbl_pos = 0;
static JANET_THREAD_LOCAL int gbl_cols = 80;
static JANET_THREAD_LOCAL char *gbl_history[JANET_HISTORY_MAX];
static JANET_THREAD_LOCAL int gbl_history_count = 0;
static JANET_THREAD_LOCAL int gbl_historyi = 0;
static JANET_THREAD_LOCAL JanetByteView gbl_matches[JANET_MATCH_MAX];
static JANET_THREAD_LOCAL int gbl_match_count = 0;
static JANET_THREAD_LOCAL int gbl_lines_below = 0;
#endif
/* Fallback */
#if defined(JANET_SIMPLE_GETLINE)
void janet_line_init() {
;
@@ -105,6 +127,80 @@ void janet_line_get(const char *p, JanetBuffer *buffer) {
simpleline(buffer);
}
/* Rich implementation */
#else
/* Windows */
#ifdef _WIN32
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <io.h>
static void setup_console_output(void) {
/* Enable color console on windows 10 console and utf8 output and other processing */
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD dwMode = 0;
GetConsoleMode(hOut, &dwMode);
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
SetConsoleMode(hOut, dwMode);
SetConsoleOutputCP(65001);
}
/* Ansi terminal raw mode */
static int rawmode(void) {
if (gbl_israwmode) return 0;
HANDLE hOut = GetStdHandle(STD_INPUT_HANDLE);
DWORD dwMode = 0;
GetConsoleMode(hOut, &dwMode);
dwMode &= ~ENABLE_LINE_INPUT;
dwMode &= ~ENABLE_INSERT_MODE;
dwMode &= ~ENABLE_ECHO_INPUT;
dwMode |= ENABLE_VIRTUAL_TERMINAL_INPUT;
dwMode &= ~ENABLE_PROCESSED_INPUT;
if (!SetConsoleMode(hOut, dwMode)) return 1;
gbl_israwmode = 1;
return 0;
}
/* Disable raw mode */
static void norawmode(void) {
if (!gbl_israwmode) return;
HANDLE hOut = GetStdHandle(STD_INPUT_HANDLE);
DWORD dwMode = 0;
GetConsoleMode(hOut, &dwMode);
dwMode |= ENABLE_LINE_INPUT;
dwMode |= ENABLE_INSERT_MODE;
dwMode |= ENABLE_ECHO_INPUT;
dwMode &= ~ENABLE_VIRTUAL_TERMINAL_INPUT;
dwMode |= ENABLE_PROCESSED_INPUT;
SetConsoleMode(hOut, dwMode);
gbl_israwmode = 0;
}
static long write_console(const char *bytes, size_t n) {
DWORD nwritten = 0;
BOOL result = WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), bytes, (DWORD) n, &nwritten, NULL);
if (!result) return -1; /* error */
return (long)nwritten;
}
static long read_console(char *into, size_t n) {
DWORD numread;
BOOL result = ReadConsole(GetStdHandle(STD_INPUT_HANDLE), into, (DWORD) n, &numread, NULL);
if (!result) return -1; /* error */
return (long)numread;
}
static int check_simpleline(JanetBuffer *buffer) {
if (!_isatty(_fileno(stdin)) || rawmode()) {
simpleline(buffer);
return 1;
}
return 0;
}
/* Posix */
#else
@@ -125,24 +221,7 @@ https://github.com/antirez/linenoise/blob/master/linenoise.c
#include <string.h>
#include <signal.h>
/* static state */
#define JANET_LINE_MAX 1024
#define JANET_MATCH_MAX 256
#define JANET_HISTORY_MAX 100
static JANET_THREAD_LOCAL int gbl_israwmode = 0;
static JANET_THREAD_LOCAL const char *gbl_prompt = "> ";
static JANET_THREAD_LOCAL int gbl_plen = 2;
static JANET_THREAD_LOCAL char gbl_buf[JANET_LINE_MAX];
static JANET_THREAD_LOCAL int gbl_len = 0;
static JANET_THREAD_LOCAL int gbl_pos = 0;
static JANET_THREAD_LOCAL int gbl_cols = 80;
static JANET_THREAD_LOCAL char *gbl_history[JANET_HISTORY_MAX];
static JANET_THREAD_LOCAL int gbl_history_count = 0;
static JANET_THREAD_LOCAL int gbl_historyi = 0;
static JANET_THREAD_LOCAL struct termios gbl_termios_start;
static JANET_THREAD_LOCAL JanetByteView gbl_matches[JANET_MATCH_MAX];
static JANET_THREAD_LOCAL int gbl_match_count = 0;
static JANET_THREAD_LOCAL int gbl_lines_below = 0;
/* Unsupported terminal list from linenoise */
static const char *badterms[] = {
@@ -152,15 +231,6 @@ static const char *badterms[] = {
NULL
};
static char *sdup(const char *s) {
size_t len = strlen(s) + 1;
char *mem = janet_malloc(len);
if (!mem) {
return NULL;
}
return memcpy(mem, s, len);
}
/* Ansi terminal raw mode */
static int rawmode(void) {
struct termios t;
@@ -186,13 +256,53 @@ static void norawmode(void) {
gbl_israwmode = 0;
}
static int checktermsupport() {
const char *t = getenv("TERM");
int i;
if (!t) return 1;
for (i = 0; badterms[i]; i++)
if (!strcmp(t, badterms[i])) return 0;
return 1;
}
static long write_console(char *bytes, size_t n) {
return write(STDOUT_FILENO, bytes, n);
}
static long read_console(char *into, size_t n) {
return read(STDIN_FILENO, into, n);
}
static int check_simpleline(JanetBuffer *buffer) {
if (!isatty(STDIN_FILENO) || !checktermsupport()) {
simpleline(buffer);
return 1;
}
if (rawmode()) {
simpleline(buffer);
return 1;
}
return 0;
}
#endif
static char *sdup(const char *s) {
size_t len = strlen(s) + 1;
char *mem = janet_malloc(len);
if (!mem) {
return NULL;
}
return memcpy(mem, s, len);
}
static int curpos(void) {
char buf[32];
int cols, rows;
unsigned int i = 0;
if (write(STDOUT_FILENO, "\x1b[6n", 4) != 4) return -1;
if (write_console("\x1b[6n", 4) != 4) return -1;
while (i < sizeof(buf) - 1) {
if (read(STDIN_FILENO, buf + i, 1) != 1) break;
if (read_console(buf + i, 1) != 1) break;
if (buf[i] == 'R') break;
i++;
}
@@ -203,18 +313,23 @@ static int curpos(void) {
}
static int getcols(void) {
#ifdef _WIN32
CONSOLE_SCREEN_BUFFER_INFO csbi;
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
return (int)(csbi.srWindow.Right - csbi.srWindow.Left + 1);
#else
struct winsize ws;
if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {
int start, cols;
start = curpos();
if (start == -1) goto failed;
if (write(STDOUT_FILENO, "\x1b[999C", 6) != 6) goto failed;
if (write_console("\x1b[999C", 6) != 6) goto failed;
cols = curpos();
if (cols == -1) goto failed;
if (cols > start) {
char seq[32];
snprintf(seq, 32, "\x1b[%dD", cols - start);
if (write(STDOUT_FILENO, seq, strlen(seq)) == -1) {
if (write_console(seq, strlen(seq)) == -1) {
exit(1);
}
}
@@ -224,10 +339,11 @@ static int getcols(void) {
}
failed:
return 80;
#endif
}
static void clear(void) {
if (write(STDOUT_FILENO, "\x1b[H\x1b[2J", 7) <= 0) {
if (write_console("\x1b[H\x1b[2J", 7) <= 0) {
exit(1);
}
}
@@ -259,7 +375,7 @@ static void refresh(void) {
/* Move cursor to original position. */
snprintf(seq, 64, "\r\x1b[%dC", (int)(_pos + gbl_plen));
janet_buffer_push_cstring(&b, seq);
if (write(STDOUT_FILENO, b.data, b.count) == -1) {
if (write_console((char *) b.data, b.count) == -1) {
exit(1);
}
janet_buffer_deinit(&b);
@@ -285,7 +401,7 @@ static int insert(char c, int draw) {
if (gbl_plen + gbl_len < gbl_cols) {
/* Avoid a full update of the line in the
* trivial case. */
if (write(STDOUT_FILENO, &c, 1) == -1) return -1;
if (write_console(&c, 1) == -1) return -1;
} else {
refresh();
}
@@ -312,7 +428,7 @@ static void historymove(int delta) {
gbl_historyi = gbl_history_count - 1;
}
strncpy(gbl_buf, gbl_history[gbl_historyi], JANET_LINE_MAX - 1);
gbl_pos = gbl_len = strlen(gbl_buf);
gbl_pos = gbl_len = (int) strlen(gbl_buf);
gbl_buf[gbl_len] = '\0';
refresh();
@@ -527,6 +643,7 @@ static void check_specials(JanetByteView src) {
check_cmatch(src, "unquote");
check_cmatch(src, "var");
check_cmatch(src, "while");
check_cmatch(src, "upscope");
}
static void resolve_format(JanetTable *entry) {
@@ -740,14 +857,14 @@ static int line() {
addhistory();
if (write(STDOUT_FILENO, gbl_prompt, gbl_plen) == -1) return -1;
if (write_console((char *) gbl_prompt, gbl_plen) == -1) return -1;
for (;;) {
char c;
char seq[3];
int rc;
do {
rc = read(STDIN_FILENO, &c, 1);
rc = read_console(&c, 1);
} while (rc < 0 && errno == EINTR);
if (rc <= 0) return -1;
@@ -764,8 +881,13 @@ static int line() {
kleft();
break;
case 3: /* ctrl-c */
clearlines();
norawmode();
#ifdef _WIN32
ExitProcess(1);
#else
kill(getpid(), SIGINT);
#endif
/* fallthrough */
case 17: /* ctrl-q */
gbl_cancel_current_repl_form = 1;
@@ -826,23 +948,25 @@ static int line() {
case 23: /* ctrl-w */
kbackspacew();
break;
#ifndef _WIN32
case 26: /* ctrl-z */
norawmode();
kill(getpid(), SIGSTOP);
rawmode();
refresh();
break;
#endif
case 27: /* escape sequence */
/* Read the next two bytes representing the escape sequence.
* Use two calls to handle slow terminals returning the two
* chars at different times. */
if (read(STDIN_FILENO, seq, 1) == -1) break;
if (read_console(seq, 1) == -1) break;
/* Esc[ = Control Sequence Introducer (CSI) */
if (seq[0] == '[') {
if (read(STDIN_FILENO, seq + 1, 1) == -1) break;
if (read_console(seq + 1, 1) == -1) break;
if (seq[1] >= '0' && seq[1] <= '9') {
/* Extended escape, read additional byte. */
if (read(STDIN_FILENO, seq + 2, 1) == -1) break;
if (read_console(seq + 2, 1) == -1) break;
if (seq[2] == '~') {
switch (seq[1]) {
case '1': /* Home */
@@ -861,7 +985,7 @@ static int line() {
}
}
} else if (seq[0] == 'O') {
if (read(STDIN_FILENO, seq + 1, 1) == -1) break;
if (read_console(seq + 1, 1) == -1) break;
switch (seq[1]) {
default:
break;
@@ -944,28 +1068,12 @@ void janet_line_deinit() {
gbl_historyi = 0;
}
static int checktermsupport() {
const char *t = getenv("TERM");
int i;
if (!t) return 1;
for (i = 0; badterms[i]; i++)
if (!strcmp(t, badterms[i])) return 0;
return 1;
}
void janet_line_get(const char *p, JanetBuffer *buffer) {
gbl_prompt = p;
buffer->count = 0;
gbl_historyi = 0;
if (check_simpleline(buffer)) return;
FILE *out = janet_dynfile("err", stderr);
if (!isatty(STDIN_FILENO) || !checktermsupport()) {
simpleline(buffer);
return;
}
if (rawmode()) {
simpleline(buffer);
return;
}
if (line()) {
norawmode();
fputc('\n', out);
@@ -981,6 +1089,13 @@ void janet_line_get(const char *p, JanetBuffer *buffer) {
replacehistory();
}
static void clear_at_exit(void) {
if (!gbl_israwmode) {
clearlines();
norawmode();
}
}
#endif
/*
@@ -993,18 +1108,11 @@ int main(int argc, char **argv) {
JanetTable *env;
#ifdef _WIN32
/* Enable color console on windows 10 console and utf8 output. */
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD dwMode = 0;
GetConsoleMode(hOut, &dwMode);
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
SetConsoleMode(hOut, dwMode);
SetConsoleOutputCP(65001);
setup_console_output();
#endif
#if !defined(JANET_WINDOWS) && !defined(JANET_SIMPLE_GETLINE)
/* Try and not leave the terminal in a bad state */
atexit(norawmode);
#if !defined(JANET_SIMPLE_GETLINE)
atexit(clear_at_exit);
#endif
#if defined(JANET_PRF)

View File

@@ -93,5 +93,14 @@
(assert (= 10 (named-opt-arguments 1 :a 2 :b 3 :c 4)) "named arguments 2")
(let [b @""]
(defn dummy [a b c]
(+ a b c))
(trace dummy)
(defn errout [arg]
(buffer/push b arg))
(assert (= 6 (with-dyns [*err* errout] (dummy 1 2 3))) "trace to custom err function")
(assert (deep= @"trace (dummy 1 2 3)\n" b) "trace buffer correct"))
(end-suite)

View File

@@ -33,6 +33,7 @@
# FFI check
(compwhen has-ffi
(ffi/context))
(compwhen has-ffi
(ffi/defbind memcpy :ptr [dest :ptr src :ptr n :size]))
(compwhen has-ffi

30
test/suite0013.janet Normal file
View File

@@ -0,0 +1,30 @@
# Copyright (c) 2022 Calvin Rose & 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 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.
(import ./helper :prefix "" :exit true)
(start-suite 13)
(assert (deep= (tabseq [i :in (range 3)] i (* 3 i))
@{0 0 1 3 2 6}))
(assert (deep= (tabseq [i :in (range 3)] i)
@{}))
(end-suite)