Port to Chicken 6

This commit is contained in:
Daniel Ziltener 2024-09-29 23:49:37 +02:00
parent 3738d38d5b
commit 4f8880068c
Signed by: zilti
GPG key ID: B38976E82C9DAE42
10 changed files with 300 additions and 1274 deletions

3
.envrc
View file

@ -1 +1,2 @@
use nix -p chicken chickenPackages_5.chickenEggs.apropos chickenPackages_5.chickenEggs.chicken-doc chickenPackages_5.chickenEggs.srfi-1 chickenPackages_5.chickenEggs.srfi-18 chickenPackages_5.chickenEggs.lsp-server chickenPackages_5.chickenEggs.srfi-34 chickenPackages_5.chickenEggs.srfi-35 chickenPackages_5.chickenEggs.srfi-158 chicken chickenPackages_5.chickenEggs.apropos chickenPackages_5.chickenEggs.chicken-doc chickenPackages_5.chickenEggs.srfi-1 chickenPackages_5.chickenEggs.srfi-18 chickenPackages_5.chickenEggs.lsp-server chickenPackages_5.chickenEggs.test
export NIXPKGS_ALLOW_BROKEN=1
use nix

View file

@ -1,217 +1,5 @@
# Created 2024-09-14 Sat 14:52
#+title: SRFI-180
#+author: Daniel Ziltener
#+export_file_name: README.org
#+property: header-args:scheme :session *chicken* :comments none
#+property: header-args:fundamental :eval no
* Dependencies
Main dependencies:
#+name: dependencies
| Egg | Description |
|----------+--------------------|
| srfi-34 | Exception Handling |
| srfi-35 | Exception Types |
| srfi-158 | Generators |
|----------+--------------------|
Test dependencies:
#+name: test-dependencies
| Egg | Description |
|------+--------------------------------|
| test | The de-facto standard test egg |
* API
** Exceptions
This library defines an SRFI-35 exception type ~&json-error~ that gets raised when invalid tokens are encountered. The exception type has a field ~json-invalid-token~ that contains the offending token.
#+begin_src scheme
(define-condition-type &json-error &error
json-error?
(json-error-reason json-error-reason)
(json-invalid-token json-invalid-token))
#+end_src
** Parameters
This library offers the following configuration parameters:
#+name: parameters
| Parameter | Default | Description |
|--------------------------------+---------+-----------------------------------------------------|
| json-nesting-depth-limit | +inf.0 | the maximum nesting depth of JSON that can be read. |
| json-number-of-character-limit | +inf.0 | the maximum length of JSON input that can be read. |
** Predicates
For some reason, this SRFI includes a predicate to check for JSON null values:
#+begin_src scheme
(define (json-null? obj) (eq? obj 'null))
#+end_src
** Reading JSON
*** json-generator
~(json-generator [port-or-generator]) → generator~
Streaming event-based JSON reader. =PORT-OR-GENERATOR= default value is the value returned by =current-input-port=. It must be a textual input port or a generator of characters. =json-generator= returns a generator of Scheme objects, each of which must be one of:
- ~'array-start~ symbol denoting that an array should be constructed.
- ~'array-end~ symbol denoting that the construction of the array for which the last ~'array-start~ was generated and not closed is finished.
- ~'object-start~ symbol denoting that an object should be constructed. The object's key-value pairs are emitted in sequence like those in a property list (plist) where keys are strings. That is, the generation of a key is always followed by the generation of a value. Otherwise, the JSON would be invalid and =json-generator= would raise an error.
- ~'object-end~ symbol denoting that the construction of the object for which the last ~'object-start~ was generated and not closed is finished.
- the symbol ~'null~
- boolean
- number
- string
In the case where nesting of arrays or objects reaches the value returned by the parameter =json-nesting-depth-limit=, the generator must raise an object that satisfies the predicate =json-error?=.
In cases where the JSON is invalid, the generator returned by =json-generator= should raise an object that satisfies the predicate =json-error?=.
Otherwise, if =PORT-OR-GENERATOR= contains valid JSON text, the generator returned by =json-generator= must yield an end-of-file object in two situations:
- The first time the generator returned by =json-generator= is called, it returns an object that is a boolean, a number, a string or the symbol ='null=.
- The first time the generator returned by =json-generator= is called, it returns a symbol that is not the symbol ='null=. When the underlying JSON text is valid, it should be the symbol starting a structure: ='object-start= or ='array-start=. The end-of-file object is generated when that structure is finished.
In other words, the generator returned by =json-generator= will parse at most one JSON value or one top-level structure. If =PORT= is not finished, as in the case of JSON lines, the user should call =json-generator= again with the same =PORT-OR-GENERATOR=.
**** Examples
#+begin_src scheme
(call-with-input-string "42 101 1337" (lambda (port) (generator->list (json-generator port))))
#+end_src
#+results:
#+begin_src scheme
(42)
#+end_src
#+begin_src scheme
(call-with-input-string "[42] 101 1337" (lambda (port) (generator->list (json-generator port))))
#+end_src
#+results:
#+begin_src scheme
(array-start 42 array-end)
#+end_src
*** json-fold
~(json-fold proc array-start array-end object-start object-end seed [port-or-generator])~
Fundamental JSON iterator.
=json-fold= will read the JSON text from =PORT-OR-GENERATOR=, which has ~(current-input-port)~ as its default value. =json-fold= will call the procedures passed as argument:
- ~(PROC obj seed)~ is called when a JSON value is generated or a complete JSON structure is read. =PROC= should return the new seed that will be used to iterate over the rest of the generator. Termination is described below.
- ~(OBJECT-START seed)~ is called with a seed and should return a seed that will be used as the seed of the iteration over the key and values of that object.
- ~(OBJECT-END seed)~ is called with a seed and should return a new seed that is the result of the iteration over a JSON object.
=ARRAY-START= and =ARRAY-END= take the same arguments, and have similar behavior, but are called for iterating on JSON arrays.
=json-fold= must return the seed when:
- =PORT-OR-GENERATOR= yields an object that satisfies the predicate =eof-object?=
- All structures, array or object, that were started have ended. The returned object is ~(PROC obj SEED)~ where obj is the object returned by =ARRAY-END= or =OBJECT-END=
*** json-read
~(json-read [port-or-generator]) → object~
JSON reader procedure. =PORT-OR-GENERATOR= must be a textual input port or a generator of characters. The default value of =PORT-OR-GENERATOR= is the value returned by the procedure =current-input-port=. The returned value is a Scheme object. =json-read= must return only the first toplevel JSON value or structure. When there are multiple toplevel values or structures in =PORT-OR-GENERATOR=, the user should call =json-read= several times to read all of it.
The mapping between JSON types and Scheme objects is the following:
- =null= → the symbol ='null=
- =true==#t=
- =false==#f=
- =number= → number
- =string= → string
- =array= → vector
- =object= → association list with keys that are symbols
In the case where nesting of arrays or objects reaches the value returned by the parameter =json-nesting-depth-limit=, =json-read= must raise an object that satisfies the predicate =json-error?=
*** json-lines-read
~(json-lines-read [port-or-generator]) → generator~
JSON reader of jsonlines or ndjson. As its first and only argument, it takes a generator of characters or a textual input port whose default value is the value returned by =current-input-port=. It will return a generator of Scheme objects as specified in =json-read=.
*** json-sequence-read
~(json-sequence-read [port-or-generator]) → generator~
JSON reader of JSON Text Sequences (RFC 7464). As its first and only argument, it takes a generator of characters or a textual input port whose default value is the value returned by =current-input-port=. It will return a generator of Scheme objects as specified in =json-read=.
*** json-accumulator
~(json-accumulator port-or-accumulator) → procedure~
Streaming event-based JSON writer. =PORT-OR-ACCUMULATOR= must be a textual output port or an accumulator that accepts characters and strings. It returns an accumulator procedure that accepts Scheme objects as its first and only argument and that follows the same protocol as described in =json-generator=. Any deviation from the protocol must raise an error that satisfies =json-error?=. In particular, objects and arrays must be properly nested.
Mind the fact that most JSON parsers have a nesting limit that is not documented by the standard. Even if you can produce arbitrarily nested JSON with this library, you might not be able to read it with another library.
*** json-write
~(json-write obj [port-or-accumulator]) → unspecified~
JSON writer procedure. =PORT-OR-ACCUMULATOR= must be a textual output port, or an accumulator that accepts characters and strings. The default value of =PORT-OR-ACCUMULATOR= is the value returned by the procedure =current-output-port=. The value returned by =json-write= is unspecified.
=json-write= will validate that =OBJ= can be serialized into JSON before writing to =PORT=. An error that satisfies =json-error?= is raised in the case where =OBJ= is not an object or a composition of the following types:
- symbol ='null=
- boolean
- number. Must be integers or inexact rationals. (That is, they must not be complex, infinite, NaN, or exact rationals that are not integers.)
- string
- vector
- association list with keys as symbols
* About this egg
** Source
The source is available at [[https://forgejo.lyrion.ch/Chicken/srfi-180]].
** Author
Daniel Ziltener
** Version History
#+name: version-history
| 1.5.1 | Escape sequences |
| 1.5.0 | Reimplementation |
| 1.0.0 | Reference Implementation |
* License
#+begin_src fundamental
Copyright (C) 2022 Daniel Ziltener
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
,* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
,* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
,* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#+end_src
This is a port of SRFI-180 to Chicken Scheme. For documentation, see [[https://wiki.call-cc.org/eggref/5/srfi-180][the Chicken wiki]].

126
nix/chicken.nix Normal file
View file

@ -0,0 +1,126 @@
{
pkgs,
lib,
stdenv,
fetchgit,
fetchurl,
makeWrapper,
darwin,
tcc-mob,
version ? "git",
testers
}:
let
platform = with stdenv;
if isDarwin then "macosx"
else if isCygwin then "cygwin"
else if (isFreeBSD || isOpenBSD) then "bsd"
else if isSunOS then "solaris"
else "linux"; # Should be a sane default
in
stdenv.mkDerivation (finalAttrs: {
pname = "chicken";
inherit version;
binaryVersion = 12;
srcs = [
(fetchgit {
url = "git://code.call-cc.org/chicken-core";
rev = "8c16ffb605a89d2f1580e01696d673e71fa1f6c4";
sha256 = "sha256-y7vFeglKJYyTyW91bvp0ZrcQF18S0SGCZ1rNCx36W4w=";
})
(fetchurl {
url = "https://code.call-cc.org/dev-snapshots/2024/07/01/chicken-6.0.0-bootstrap.tar.gz";
sha256 = "sha256-qkcyWzsaN9+HbMBolmv7zeaPrtbaCTGa9HoF2g/3//o=";
})
];
unpackPhase = ''
cp -r `echo $srcs | awk '{print $1}'`/* .
cp -r `echo $srcs | awk '{print $1}'`/.* .
chmod -R 777 .
mkdir -p boot/snapshot
cd boot
tar xzf `echo $srcs | awk '{print $2}'`
cd ..
echo ${version} > buildid
cd boot/chicken-6.0.0
case "${platform}" in
bsd)
mkcmd=gmake;;
*)
mkcmd=make;;
esac
export CC="${tcc-mob}/bin/tcc"
$mkcmd C_COMPILER=$CC PREFIX="$(pwd)"/../snapshot
$mkcmd C_COMPILER=$CC PREFIX="$(pwd)"/../snapshot install
cd ../..
./configure --chicken "$(pwd)"/boot/snapshot/bin/chicken --c-compiler "${tcc-mob}/bin/tcc"
$mkcmd boot-chicken
'';
# Disable two broken tests: "static link" and "linking tests"
postPatch = ''
sed -i tests/runtests.sh -e "/static link/,+4 { s/^/# / }"
sed -i tests/runtests.sh -e "/linking tests/,+11 { s/^/# / }"
'';
# -fno-strict-overflow is not a supported argument in clang
hardeningDisable = lib.optionals stdenv.cc.isClang [ "strictoverflow" ];
makeFlags = [
"PLATFORM=${platform}"
"PREFIX=$(out)"
"C_COMPILER=${tcc-mob}/bin/tcc"
"CXX_COMPILER=$(CXX)"
] ++ (lib.optionals stdenv.isDarwin [
"XCODE_TOOL_PATH=${darwin.binutils.bintools}/bin"
"LINKER_OPTIONS=-headerpad_max_install_names"
"POSTINSTALL_PROGRAM=install_name_tool"
]) ++ (lib.optionals (stdenv.hostPlatform != stdenv.buildPlatform) [
"HOSTSYSTEM=${stdenv.hostPlatform.config}"
"TARGET_C_COMPILER=${tcc-mob}/bin/${stdenv.cc.targetPrefix}tcc"
"TARGET_CXX_COMPILER=${stdenv.cc}/bin/${stdenv.cc.targetPrefix}c++"
]);
nativeBuildInputs = [
makeWrapper
pkgs.hostname
tcc-mob
] ++ lib.optionals (stdenv.isDarwin && stdenv.isAarch64) [
darwin.autoSignDarwinBinariesHook
];
configurePhase = ''
./configure --chicken ./chicken-boot --prefix $PREFIX --platform=$PLATFORM --c-compiler "${tcc-mob}/bin/tcc"
'';
doCheck = !stdenv.isDarwin;
postCheck = ''
./csi -R chicken.pathname -R chicken.platform \
-p "(assert (equal? \"${toString finalAttrs.binaryVersion}\" (pathname-file (car (repository-path)))))"
'';
passthru.tests.version = testers.testVersion {
package = finalAttrs.finalPackage;
command = "csi -version";
};
meta = {
homepage = "https://call-cc.org/";
license = lib.licenses.bsd3;
maintainers = with lib.maintainers; [ corngood nagy konst-aa ];
platforms = lib.platforms.unix;
description = "Portable compiler for the Scheme programming language";
longDescription = ''
CHICKEN is a compiler for the Scheme programming language.
CHICKEN produces portable and efficient C, supports almost all
of the R5RS Scheme language standard, and includes many
enhancements and extensions. CHICKEN runs on Linux, macOS,
Windows, and many Unix flavours.
'';
};
})

129
nix/tinycc.nix Normal file
View file

@ -0,0 +1,129 @@
{ lib
, copyPkgconfigItems
, fetchFromRepoOrCz
, makePkgconfigItem
, perl
, stdenv
, texinfo
, which
}:
stdenv.mkDerivation (finalAttrs: {
pname = "tcc-mob";
version = "0.9.29-unstable-2024-09-16";
src = fetchFromRepoOrCz {
repo = "tinycc";
rev = "b8b6a5fd7b4e8cab8e5a5d01064cf5bf2b5eed95";
hash = "sha256-jY0P2GErmo//YBaz6u4/jj/voOE3C2JaIDRmo0orXN8=";
};
outputs = [ "out" "info" "man" ];
nativeBuildInputs = [
copyPkgconfigItems
perl
texinfo
which
];
strictDeps = true;
pkgconfigItems = let
libtcc-pcitem = {
name = "libtcc";
inherit (finalAttrs) version;
cflags = [ "-I${libtcc-pcitem.variables.includedir}" ];
libs = [
"-L${libtcc-pcitem.variables.libdir}"
"-Wl,--rpath ${libtcc-pcitem.variables.libdir}"
"-ltcc"
];
variables = {
prefix = "${placeholder "out"}";
includedir = "${placeholder "dev"}/include";
libdir = "${placeholder "lib"}/lib";
};
description = "Tiny C compiler backend";
};
in [
(makePkgconfigItem libtcc-pcitem)
];
postPatch = ''
patchShebangs texi2pod.pl
'';
configureFlags = [
"--cc=$CC"
"--ar=$AR"
"--crtprefix=${lib.getLib stdenv.cc.libc}/lib"
"--sysincludepaths=${lib.getDev stdenv.cc.libc}/include:{B}/include"
"--libpaths=${lib.getLib stdenv.cc.libc}/lib"
# build cross compilers
"--enable-cross"
] ++ lib.optionals stdenv.hostPlatform.isMusl [
"--config-musl"
];
preConfigure = let
# To avoid "malformed 32-bit x.y.z" error on mac when using clang
versionIsClean = version:
builtins.match "^[0-9]\\.+[0-9]+\\.[0-9]+" version != null;
in ''
${
if stdenv.isDarwin && ! versionIsClean finalAttrs.version
then "echo 'not overwriting VERSION since it would upset ld'"
else "echo ${finalAttrs.version} > VERSION"
}
configureFlagsArray+=("--elfinterp=$(< $NIX_CC/nix-support/dynamic-linker)")
'';
env.NIX_CFLAGS_COMPILE = toString (lib.optionals stdenv.cc.isClang [
"-Wno-error=implicit-int"
"-Wno-error=int-conversion"
]);
# Test segfault for static build
doCheck = !stdenv.hostPlatform.isStatic;
checkTarget = "test";
# https://www.mail-archive.com/tinycc-devel@nongnu.org/msg10142.html
preCheck = lib.optionalString (stdenv.isDarwin && stdenv.isx86_64) ''
rm tests/tests2/{108,114}*
'';
meta = {
homepage = "https://repo.or.cz/tinycc.git";
description = "Small, fast, and embeddable C compiler and interpreter";
longDescription = ''
TinyCC (aka TCC) is a small but hyper fast C compiler. Unlike other C
compilers, it is meant to be self-sufficient: you do not need an external
assembler or linker because TCC does that for you.
TCC compiles so fast that even for big projects Makefiles may not be
necessary.
TCC not only supports ANSI C, but also most of the new ISO C99 standard
and many GNU C extensions.
TCC can also be used to make C scripts, i.e. pieces of C source that you
run as a Perl or Python script. Compilation is so fast that your script
will be as fast as if it was an executable.
TCC can also automatically generate memory and bound checks while allowing
all C pointers operations. TCC can do these checks even if non patched
libraries are used.
With libtcc, you can use TCC as a backend for dynamic code generation.
'';
license = with lib.licenses; [ lgpl21Only ];
mainProgram = "tcc";
maintainers = with lib.maintainers; [ joachifm AndersonTorres ];
platforms = lib.platforms.unix;
# https://www.mail-archive.com/tinycc-devel@nongnu.org/msg10199.html
broken = stdenv.isDarwin && stdenv.isAarch64;
};
})
# TODO: more multiple outputs
# TODO: self-compilation

34
shell.nix Normal file
View file

@ -0,0 +1,34 @@
with import <nixpkgs> {
overlays = [
(final: prev: {
tcc-mob = final.callPackage ./nix/tinycc.nix { stdenv = final.gcc13Stdenv; };
chicken = final.callPackage ./nix/chicken.nix {
stdenv = final.gcc13Stdenv;
version = "6.0.0-8c16ffb";
};
})
];
};
mkShell {
packages = with pkgs; [
tcc-mob
chicken
]
# ++ (with pkgs.chickenPackages_5.chickenEggs; [
# apropos
# chicken-doc
# srfi-1
# srfi-18
# lsp-server
# srfi-152
# ])
;
shellHook = ''
export CC="${pkgs.tcc-mob}/bin/tcc"
export CHICKEN_INSTALL_PREFIX="$HOME/.chicken"
export CHICKEN_INSTALL_REPOSITORY="$HOME/.chicken/eggs"
export CHICKEN_REPOSITORY_PATH="${pkgs.chicken}/lib/chicken/12:$HOME/.chicken/eggs"
export PATH="$PATH:$CHICKEN_PREFIX"
export CHICKEN_INSTALL_PREFIX="${pkgs.chicken}"
'';
}

View file

@ -3,7 +3,7 @@
(synopsis "A JSON parser and printer that supports JSON bigger than memory.")
(category parsing)
(license "BSD")
(version "1.5.2")
(version "1.5.3")
(dependencies srfi-34 srfi-35 srfi-158)
(test-dependencies test)
(components

View file

@ -1,8 +1,9 @@
(import
(scheme)
(only (scheme base) make-parameter)
(chicken format)
(chicken port)
(chicken string)
(only (chicken string) reverse-list->string)
(srfi-34) ;;Exception Handling
(srfi-35) ;;Exception Types
(srfi-158) ;;Generators

File diff suppressed because it is too large Load diff

4
srfi-180.release-info.6 Normal file
View file

@ -0,0 +1,4 @@
;; -*- Scheme -*-
(repo git "https://forgejo.lyrion.ch/Chicken/srfi-180.git")
(uri targz "https://forgejo.lyrion.ch/Chicken/srfi-180/archive/{egg-release}.tar.gz")
(release "1.5.3") ;; Port to Chicken 6

View file

@ -14,7 +14,7 @@
json-accumulator
json-write)
(import (scheme)
(chicken base)
(chicken base)
(chicken platform))
(register-feature! 'srfi-180)
(include-relative "srfi-180.impl.scm"))