Remove website
The website now lives in a separate repo: https://github.com/tweag/nickel-lang.org.
40
website/.github/settings.yml
vendored
@ -1,40 +0,0 @@
|
||||
repository:
|
||||
has_wiki: false
|
||||
|
||||
labels:
|
||||
- name: "duplicate"
|
||||
color: cfd3d7
|
||||
- name: "good first issue"
|
||||
color: 7057ff
|
||||
- name: "invalid"
|
||||
color: cfd3d7
|
||||
- name: "more data needed"
|
||||
color: bfdadc
|
||||
- name: "P0"
|
||||
color: b60205
|
||||
description: "blocker: fix immediately!"
|
||||
- name: "P1"
|
||||
color: d93f0b
|
||||
description: "critical: next release"
|
||||
- name: "P2"
|
||||
color: e99695
|
||||
description: "major: an upcoming release"
|
||||
- name: "P3"
|
||||
color: fbca04
|
||||
description: "minor: not priorized"
|
||||
- name: "P4"
|
||||
color: fef2c0
|
||||
description: "unimportant: consider wontfix or other priority"
|
||||
- name: "question"
|
||||
color: d876e3
|
||||
- name: "type: bug"
|
||||
color: 0052cc
|
||||
- name: "type: documentation"
|
||||
color: 0052cc
|
||||
- name: "type: feature request"
|
||||
color: 0052cc
|
||||
- name: "wontfix"
|
||||
color: ffffff
|
||||
- name: "merge-queue"
|
||||
color: 0e8a16
|
||||
description: "merge on green CI"
|
4
website/.gitignore
vendored
@ -1,4 +0,0 @@
|
||||
node_modules/
|
||||
.cache/
|
||||
public
|
||||
.idea
|
@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) Tweag Holding and its affiliates.
|
||||
|
||||
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.
|
@ -1,4 +0,0 @@
|
||||
# Nickel-lang.org website
|
||||
|
||||
This repository contains the content of the Nickel language website. This is
|
||||
WIP.
|
@ -1,5 +0,0 @@
|
||||
import "./src/styles/custom.scss";
|
||||
import "jquery/dist/jquery.min.js";
|
||||
import "popper.js/dist/popper.min";
|
||||
import "bootstrap/dist/js/bootstrap.min.js";
|
||||
import "./src/styles/global.css";
|
@ -1,35 +0,0 @@
|
||||
module.exports = {
|
||||
siteMetadata: {
|
||||
title: "Nickel",
|
||||
menuLinks: [
|
||||
{
|
||||
name: 'Getting started',
|
||||
link: '/getting-started'
|
||||
},
|
||||
// Disabling the documentation page for now.
|
||||
// There is just not enough interesting content to show.
|
||||
/*{
|
||||
name: 'Documentation',
|
||||
link: '/documentation'
|
||||
},*/
|
||||
{
|
||||
name: 'Playground',
|
||||
link: '/playground'
|
||||
},
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
'gatsby-plugin-react-helmet',
|
||||
'gatsby-plugin-sharp',
|
||||
'gatsby-transformer-sharp',
|
||||
'gatsby-plugin-image',
|
||||
{
|
||||
resolve: `gatsby-source-filesystem`,
|
||||
options: {
|
||||
name: `markdown-pages`,
|
||||
path: `${__dirname}/src/markdown-pages`,
|
||||
},
|
||||
},
|
||||
`gatsby-plugin-sass`,
|
||||
],
|
||||
};
|
@ -1,8 +0,0 @@
|
||||
exports.onCreateWebpackConfig = ({ _stage, actions, _loaders }) => {
|
||||
actions.setWebpackConfig({
|
||||
experiments: {
|
||||
// This was necessary to have the Nickel WASM REPL work with Webpack
|
||||
asyncWebAssembly: true,
|
||||
},
|
||||
})
|
||||
};
|
@ -1,29 +0,0 @@
|
||||
[[redirects]]
|
||||
from = "https://nickel-lang.netlify.com/*"
|
||||
to = "https://nickel-lang.org/:splat"
|
||||
status = 301
|
||||
force = true
|
||||
|
||||
[[redirects]]
|
||||
from = "http://nickel-lang.netlify.com/*"
|
||||
to = "https://nickel-lang.org/:splat"
|
||||
status = 301
|
||||
force = true
|
||||
|
||||
[[redirects]]
|
||||
from = "http://www.nickel-lang.org/*"
|
||||
to = "https://nickel-lang.org/:splat"
|
||||
status = 301
|
||||
force = true
|
||||
|
||||
[[redirects]]
|
||||
from = "https://www.nickel-lang.org/*"
|
||||
to = "https://nickel-lang.org/:splat"
|
||||
status = 301
|
||||
force = true
|
||||
|
||||
[[redirects]]
|
||||
from = "http://nickel-lang.org/*"
|
||||
to = "https://nickel-lang.org/:splat"
|
||||
status = 301
|
||||
force = true
|
@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) Tweag Holding and its affiliates.
|
||||
|
||||
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.
|
@ -1,217 +0,0 @@
|
||||
# Nickel
|
||||
|
||||
[![Continuous integration](https://github.com/tweag/nickel/workflows/Continuous%20integration/badge.svg)](https://github.com/tweag/nickel/actions?query=branch%3Amaster)
|
||||
|
||||
Nickel is the cheap configuration language.
|
||||
|
||||
Its purpose is to automate the generation of static configuration files - think
|
||||
JSON, YAML, XML, or your favorite data representation language - that are then
|
||||
fed to another system. It is designed to have a simple, well-understood core: it
|
||||
is in essence JSON with functions.
|
||||
|
||||
Nickel's salient traits are:
|
||||
|
||||
- **Lightweight**: Nickel is easy to embed. An interpreter should be simple to
|
||||
implement. The reference interpreter can be called from many programming
|
||||
languages.
|
||||
- **Composable code**: the basic building blocks for computing are functions.
|
||||
They are first-class citizens, which can be passed around, called and
|
||||
composed.
|
||||
- **Composable data**: the basic building blocks for data are records
|
||||
(called *objects* in JSON). In Nickel, records can be merged at will,
|
||||
including associated metadata (documentation, default values, type
|
||||
contracts, etc).
|
||||
- **Typed, but only when it helps**: static types improve code quality, serve as
|
||||
documentation and eliminate bugs early. But application-specific
|
||||
self-contained code will always evaluate to the same value, so type errors
|
||||
will show up at runtime anyway. Some JSON is hard to type. There, types are
|
||||
only a burden. Whereas reusable code - that is, *functions* - is evaluated
|
||||
on potentially infinitely many different inputs, and is impossible to test
|
||||
exhaustively. There, types are precious. Nickel has types, but you get to
|
||||
choose when you want it or not, and it handles safely the interaction between
|
||||
the typed and the untyped world.
|
||||
- **Design by contract**: complementary to the type system, contracts are
|
||||
a principled approach to checking assertions. The interpreter automatically
|
||||
inserts assertions at the boundary between typed and untyped code. Nickel
|
||||
lets users add arbitrary assertions of their own and easily understand why
|
||||
when assertions fail.
|
||||
|
||||
The motto guiding Nickel's design is:
|
||||
> Great defaults, design for extensibility
|
||||
|
||||
There should be a standard, clear path for common things. There should be no
|
||||
arbitrary restrictions that limit what you can do you the one day you need to go
|
||||
beyond.
|
||||
|
||||
## Use cases
|
||||
|
||||
Nickel is a good fit in any situation where you need to generate a complex
|
||||
configuration, be it for a single app, a machine, whole infrastructure, or a
|
||||
build system.
|
||||
|
||||
The motivating use cases are in particular:
|
||||
- The [Nix package manager](https://nixos.org/): Nix is a declarative package
|
||||
manager using its own language for specifying packages. Nickel is an
|
||||
evolution of the Nix language, while trying to overcome some of its
|
||||
limitations.
|
||||
- Infrastructure as code: infrastructure is becoming increasingly complex,
|
||||
requiring a rigorous approach to deployment, modification and configuration.
|
||||
This is where a declarative approach also shines, as adopted by
|
||||
[Terraform](https://www.terraform.io/),
|
||||
[NixOps](https://github.com/NixOS/nixops) or
|
||||
[Kubernetes](https://kubernetes.io/), all requiring potentially complex
|
||||
generation of configuration.
|
||||
- Build systems: build systems (like [Bazel](https://bazel.build/)) need
|
||||
a specification of the dependency graph.
|
||||
|
||||
Most aforementioned projects have their own bespoke configuration language. See
|
||||
[Related projects and inspirations](#Related-projects-and-inspirations). In
|
||||
general, application-specific languages might suffer from feature creep, lack of
|
||||
abstractions or just feel ad hoc. Nickel buys you more for less.
|
||||
|
||||
## Getting started
|
||||
|
||||
### Run
|
||||
|
||||
1. Start Nickel
|
||||
* with [flake-enabled](https://nixos.wiki/wiki/Flakes) Nix directly
|
||||
with `nix run nickel` (which pulls it from the global flakes
|
||||
registry), or with `nix run github:tweag/nickel` (which pulls it
|
||||
from the repo). You can use [our binary cache](https://nickel.cachix.org) to
|
||||
prevent rebuilding a lot of packages. You pass in arguments with
|
||||
an extra `--` as in `nix run nickel -- repl`,
|
||||
* with `./nickel`, after [building](#Build) this repo, depending on the
|
||||
location of the executable and passing in arguments directly,
|
||||
* or with `cargo run` after [building](#Build), passing in arguments with
|
||||
an extra `--` as in `cargo run -- -f program.ncl`.
|
||||
|
||||
2. Run your first program:
|
||||
```console
|
||||
$ ./nickel <<< 'let x = 2 in x + x'
|
||||
Typechecked: Ok(Types(Dyn))
|
||||
Done: Num(4.0)
|
||||
```
|
||||
Or load it from a file:
|
||||
```console
|
||||
$ echo 'let s = "world" in "Hello, " ++ s' > program.ncl
|
||||
$ ./nickel -f program.ncl
|
||||
Typechecked: Ok(Types(Dyn))
|
||||
Done: Str("Hello, world")
|
||||
```
|
||||
3. Start a REPL:
|
||||
```console
|
||||
$ ./nickel repl
|
||||
nickel> let x = 2 in x + x
|
||||
4
|
||||
|
||||
nickel>
|
||||
```
|
||||
Use `:help` for a list of available commands.
|
||||
|
||||
4. Export your configuration to JSON, YAML or TOML:
|
||||
```console
|
||||
$ ./nickel export --format json <<< '{foo = "Hello, world!"}'
|
||||
{
|
||||
"foo": "Hello, world!"
|
||||
}
|
||||
```
|
||||
|
||||
Use `nickel help` for a list of subcommands, and `nickel help <subcommand>`
|
||||
for help about a specific subcommand.
|
||||
|
||||
### Build
|
||||
|
||||
[rust-guide]: https://doc.rust-lang.org/cargo/getting-started/installation.html
|
||||
|
||||
1. Download build dependencies:
|
||||
- **With Nix**: If you have [Nix](https://nixos.org/nix) installed:
|
||||
```console
|
||||
$ nix-shell shell.nix
|
||||
```
|
||||
to be dropped in a shell, ready to build. You can use [our binary
|
||||
cache](https://nickel.cachix.org) to prevent rebuilding a lot of
|
||||
packages.
|
||||
- **Without Nix**: otherwise, follow [this guide][rust-guide] to install Rust
|
||||
and Cargo first.
|
||||
2. Build Nickel:
|
||||
```console
|
||||
$ cargo build
|
||||
```
|
||||
And voilà! Generated files are placed in `target/debug`.
|
||||
|
||||
|
||||
1. *(optional)* make a symbolic link to the executable:
|
||||
```console
|
||||
$ ln -S nickel target/debug/nickel
|
||||
```
|
||||
|
||||
|
||||
### Tests
|
||||
|
||||
```console
|
||||
$ cargo test
|
||||
```
|
||||
|
||||
### Documentation
|
||||
|
||||
1. Build the doc:
|
||||
```console
|
||||
$ cargo doc --no-deps
|
||||
```
|
||||
2. Open the file `target/doc/nickel/index.html` in your browser.
|
||||
|
||||
### Examples
|
||||
|
||||
You can find examples in
|
||||
the [`./examples`](./examples) directory. Note
|
||||
that as the syntax is not yet fixed, and some basic helpers are missing, they
|
||||
may seem a bit alien currently.
|
||||
|
||||
## Roadmap
|
||||
|
||||
The design is settled and implemented for the most part, but the final syntax
|
||||
and other important practical aspects are still being debated. We aim to
|
||||
transition from an experimental stage to a minimum viable product stage. The
|
||||
next points to deal with are:
|
||||
|
||||
- [Stdlib stabilization](https://github.com/tweag/nickel/issues/321)
|
||||
- [Overriding](https://github.com/tweag/nickel/pull/330)
|
||||
- Memory management (use reference counting) & basic performance improvements
|
||||
- [List comprehensions](https://github.com/tweag/nickel/issues/80)
|
||||
- [Destructuring](https://github.com/tweag/nickel/issues/81)
|
||||
|
||||
## Related projects and inspirations
|
||||
|
||||
- [Cue](https://cuelang.org/) is a configuration language with a focus on data
|
||||
validation. It introduces a new constraint system backed by a solid theory
|
||||
which ensures strong guarantees about your code. It allows for very elegant
|
||||
schema specifications. In return, the cost to pay is to abandon functions
|
||||
and
|
||||
[Turing-completeness](https://en.wikipedia.org/wiki/Turing_completeness).
|
||||
Nickel's merge system is inspired by the one of CUE, even if since Nickel
|
||||
does have general functions and is Turing-complete, they are necessarily
|
||||
different.
|
||||
- [Nix](https://nixos.org/): The Nix language, or *Nix expressions*, is one of
|
||||
the main inspirations for Nickel. It is a very simple yet powerful lazy
|
||||
functional language. We strive to retain this simplicity, while adding
|
||||
typing capabilities, modularity, and detaching the language from the Nix
|
||||
package manager.
|
||||
- [Dhall](https://dhall-lang.org/) is a statically typed configuration language.
|
||||
It is also inspired by Nix, to which it adds a powerful static type system.
|
||||
However, this forces the programmer to annotate all of their code with types.
|
||||
- [Jsonnet](https://jsonnet.org/) is another language which could be dubbed as
|
||||
"JSON with functions" (and others things as well). It is a lazy functional
|
||||
language with object oriented features, among which inheritance is similar
|
||||
to Nickel's merge system. One big difference with Nickel is the absence of
|
||||
typing.
|
||||
- [Pulumi](https://www.pulumi.com/) is not a language in itself, but a cloud
|
||||
tool (like Terraform) where you can use your preferred language for
|
||||
describing your infrastructure. This is a different approach to the problem,
|
||||
with different trade-offs.
|
||||
- [Starlark](https://docs.bazel.build/versions/master/skylark/language.html) is
|
||||
the language of [Bazel](https://bazel.build/), which is a dialect of
|
||||
[Python](https://www.python.org/). It does not have types and recursion is
|
||||
forbidden, making it not Turing-complete.
|
||||
|
||||
See [RATIONALE.md](./RATIONALE.md) for the design rationale and a more detailed
|
||||
comparison with a selection of these languages.
|
77
website/nickel-repl/nickel.d.ts
vendored
@ -1,77 +0,0 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Return a new instance of the WASM REPL, with the standard library loaded.
|
||||
* @returns {WasmInitResult}
|
||||
*/
|
||||
export function repl_init(): WasmInitResult;
|
||||
/**
|
||||
* Evaluate an input in the WASM REPL.
|
||||
* @param {ReplState} state
|
||||
* @param {string} line
|
||||
* @returns {WasmInputResult}
|
||||
*/
|
||||
export function repl_input(state: ReplState, line: string): WasmInputResult;
|
||||
/**
|
||||
* Evaluate an input in the WASM REPL and serialize it.
|
||||
* @param {ReplState} state
|
||||
* @param {any} format
|
||||
* @param {string} line
|
||||
* @returns {WasmInputResult}
|
||||
*/
|
||||
export function repl_serialize(state: ReplState, format: any, line: string): WasmInputResult;
|
||||
/**
|
||||
* Return codes of the WASM REPL.
|
||||
*
|
||||
* wasm-bindgen doesn't support exporting arbitrary enumeration. Thus we have to encode these
|
||||
* enums as structures with a tag and values. The values that are actually set depend on the
|
||||
* tag.
|
||||
*/
|
||||
export enum WasmResultTag {
|
||||
Success,
|
||||
Blank,
|
||||
Partial,
|
||||
Error,
|
||||
}
|
||||
/**
|
||||
* WASM-compatible wrapper around `ReplImpl`.
|
||||
*/
|
||||
export class ReplState {
|
||||
free(): void;
|
||||
}
|
||||
/**
|
||||
* WASM wrapper for the result type of the initialization of the REPL.
|
||||
*/
|
||||
export class WasmInitResult {
|
||||
free(): void;
|
||||
/**
|
||||
* @returns {ReplState}
|
||||
*/
|
||||
repl(): ReplState;
|
||||
/**
|
||||
* @returns {string}
|
||||
*/
|
||||
readonly msg: string;
|
||||
/**
|
||||
* @returns {number}
|
||||
*/
|
||||
tag: number;
|
||||
}
|
||||
/**
|
||||
* WASM wrapper for the result type of an execution of the REPL.
|
||||
*/
|
||||
export class WasmInputResult {
|
||||
free(): void;
|
||||
/**
|
||||
* @returns {any}
|
||||
*/
|
||||
readonly errors: any;
|
||||
/**
|
||||
* @returns {string}
|
||||
*/
|
||||
readonly msg: string;
|
||||
/**
|
||||
* @returns {number}
|
||||
*/
|
||||
tag: number;
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
import * as wasm from "./nickel_bg.wasm";
|
||||
export * from "./nickel_bg.js";
|
@ -1,340 +0,0 @@
|
||||
import * as wasm from './nickel_bg.wasm';
|
||||
|
||||
const lTextDecoder = typeof TextDecoder === 'undefined' ? (0, module.require)('util').TextDecoder : TextDecoder;
|
||||
|
||||
let cachedTextDecoder = new lTextDecoder('utf-8', { ignoreBOM: true, fatal: true });
|
||||
|
||||
cachedTextDecoder.decode();
|
||||
|
||||
let cachegetUint8Memory0 = null;
|
||||
function getUint8Memory0() {
|
||||
if (cachegetUint8Memory0 === null || cachegetUint8Memory0.buffer !== wasm.memory.buffer) {
|
||||
cachegetUint8Memory0 = new Uint8Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachegetUint8Memory0;
|
||||
}
|
||||
|
||||
function getStringFromWasm0(ptr, len) {
|
||||
return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
|
||||
}
|
||||
|
||||
const heap = new Array(32).fill(undefined);
|
||||
|
||||
heap.push(undefined, null, true, false);
|
||||
|
||||
let heap_next = heap.length;
|
||||
|
||||
function addHeapObject(obj) {
|
||||
if (heap_next === heap.length) heap.push(heap.length + 1);
|
||||
const idx = heap_next;
|
||||
heap_next = heap[idx];
|
||||
|
||||
heap[idx] = obj;
|
||||
return idx;
|
||||
}
|
||||
|
||||
function getObject(idx) { return heap[idx]; }
|
||||
|
||||
function dropObject(idx) {
|
||||
if (idx < 36) return;
|
||||
heap[idx] = heap_next;
|
||||
heap_next = idx;
|
||||
}
|
||||
|
||||
function takeObject(idx) {
|
||||
const ret = getObject(idx);
|
||||
dropObject(idx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
let WASM_VECTOR_LEN = 0;
|
||||
|
||||
const lTextEncoder = typeof TextEncoder === 'undefined' ? (0, module.require)('util').TextEncoder : TextEncoder;
|
||||
|
||||
let cachedTextEncoder = new lTextEncoder('utf-8');
|
||||
|
||||
const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
|
||||
? function (arg, view) {
|
||||
return cachedTextEncoder.encodeInto(arg, view);
|
||||
}
|
||||
: function (arg, view) {
|
||||
const buf = cachedTextEncoder.encode(arg);
|
||||
view.set(buf);
|
||||
return {
|
||||
read: arg.length,
|
||||
written: buf.length
|
||||
};
|
||||
});
|
||||
|
||||
function passStringToWasm0(arg, malloc, realloc) {
|
||||
|
||||
if (realloc === undefined) {
|
||||
const buf = cachedTextEncoder.encode(arg);
|
||||
const ptr = malloc(buf.length);
|
||||
getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf);
|
||||
WASM_VECTOR_LEN = buf.length;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
let len = arg.length;
|
||||
let ptr = malloc(len);
|
||||
|
||||
const mem = getUint8Memory0();
|
||||
|
||||
let offset = 0;
|
||||
|
||||
for (; offset < len; offset++) {
|
||||
const code = arg.charCodeAt(offset);
|
||||
if (code > 0x7F) break;
|
||||
mem[ptr + offset] = code;
|
||||
}
|
||||
|
||||
if (offset !== len) {
|
||||
if (offset !== 0) {
|
||||
arg = arg.slice(offset);
|
||||
}
|
||||
ptr = realloc(ptr, len, len = offset + arg.length * 3);
|
||||
const view = getUint8Memory0().subarray(ptr + offset, ptr + len);
|
||||
const ret = encodeString(arg, view);
|
||||
|
||||
offset += ret.written;
|
||||
}
|
||||
|
||||
WASM_VECTOR_LEN = offset;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
function isLikeNone(x) {
|
||||
return x === undefined || x === null;
|
||||
}
|
||||
|
||||
let cachegetInt32Memory0 = null;
|
||||
function getInt32Memory0() {
|
||||
if (cachegetInt32Memory0 === null || cachegetInt32Memory0.buffer !== wasm.memory.buffer) {
|
||||
cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachegetInt32Memory0;
|
||||
}
|
||||
/**
|
||||
* Return a new instance of the WASM REPL, with the standard library loaded.
|
||||
* @returns {WasmInitResult}
|
||||
*/
|
||||
export function repl_init() {
|
||||
var ret = wasm.repl_init();
|
||||
return WasmInitResult.__wrap(ret);
|
||||
}
|
||||
|
||||
function _assertClass(instance, klass) {
|
||||
if (!(instance instanceof klass)) {
|
||||
throw new Error(`expected instance of ${klass.name}`);
|
||||
}
|
||||
return instance.ptr;
|
||||
}
|
||||
/**
|
||||
* Evaluate an input in the WASM REPL.
|
||||
* @param {ReplState} state
|
||||
* @param {string} line
|
||||
* @returns {WasmInputResult}
|
||||
*/
|
||||
export function repl_input(state, line) {
|
||||
_assertClass(state, ReplState);
|
||||
var ptr0 = passStringToWasm0(line, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
var len0 = WASM_VECTOR_LEN;
|
||||
var ret = wasm.repl_input(state.ptr, ptr0, len0);
|
||||
return WasmInputResult.__wrap(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate an input in the WASM REPL and serialize it.
|
||||
* @param {ReplState} state
|
||||
* @param {any} format
|
||||
* @param {string} line
|
||||
* @returns {WasmInputResult}
|
||||
*/
|
||||
export function repl_serialize(state, format, line) {
|
||||
_assertClass(state, ReplState);
|
||||
var ptr0 = passStringToWasm0(line, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
var len0 = WASM_VECTOR_LEN;
|
||||
var ret = wasm.repl_serialize(state.ptr, addHeapObject(format), ptr0, len0);
|
||||
return WasmInputResult.__wrap(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return codes of the WASM REPL.
|
||||
*
|
||||
* wasm-bindgen doesn't support exporting arbitrary enumeration. Thus we have to encode these
|
||||
* enums as structures with a tag and values. The values that are actually set depend on the
|
||||
* tag.
|
||||
*/
|
||||
export const WasmResultTag = Object.freeze({ Success:0,"0":"Success",Blank:1,"1":"Blank",Partial:2,"2":"Partial",Error:3,"3":"Error", });
|
||||
/**
|
||||
* WASM-compatible wrapper around `ReplImpl`.
|
||||
*/
|
||||
export class ReplState {
|
||||
|
||||
static __wrap(ptr) {
|
||||
const obj = Object.create(ReplState.prototype);
|
||||
obj.ptr = ptr;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
__destroy_into_raw() {
|
||||
const ptr = this.ptr;
|
||||
this.ptr = 0;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
free() {
|
||||
const ptr = this.__destroy_into_raw();
|
||||
wasm.__wbg_replstate_free(ptr);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* WASM wrapper for the result type of the initialization of the REPL.
|
||||
*/
|
||||
export class WasmInitResult {
|
||||
|
||||
static __wrap(ptr) {
|
||||
const obj = Object.create(WasmInitResult.prototype);
|
||||
obj.ptr = ptr;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
__destroy_into_raw() {
|
||||
const ptr = this.ptr;
|
||||
this.ptr = 0;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
free() {
|
||||
const ptr = this.__destroy_into_raw();
|
||||
wasm.__wbg_wasminitresult_free(ptr);
|
||||
}
|
||||
/**
|
||||
* @returns {number}
|
||||
*/
|
||||
get tag() {
|
||||
var ret = wasm.__wbg_get_wasminitresult_tag(this.ptr);
|
||||
return ret >>> 0;
|
||||
}
|
||||
/**
|
||||
* @param {number} arg0
|
||||
*/
|
||||
set tag(arg0) {
|
||||
wasm.__wbg_set_wasminitresult_tag(this.ptr, arg0);
|
||||
}
|
||||
/**
|
||||
* @returns {string}
|
||||
*/
|
||||
get msg() {
|
||||
try {
|
||||
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
||||
wasm.wasminitresult_msg(retptr, this.ptr);
|
||||
var r0 = getInt32Memory0()[retptr / 4 + 0];
|
||||
var r1 = getInt32Memory0()[retptr / 4 + 1];
|
||||
return getStringFromWasm0(r0, r1);
|
||||
} finally {
|
||||
wasm.__wbindgen_add_to_stack_pointer(16);
|
||||
wasm.__wbindgen_free(r0, r1);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @returns {ReplState}
|
||||
*/
|
||||
repl() {
|
||||
const ptr = this.__destroy_into_raw();
|
||||
var ret = wasm.wasminitresult_repl(ptr);
|
||||
return ReplState.__wrap(ret);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* WASM wrapper for the result type of an execution of the REPL.
|
||||
*/
|
||||
export class WasmInputResult {
|
||||
|
||||
static __wrap(ptr) {
|
||||
const obj = Object.create(WasmInputResult.prototype);
|
||||
obj.ptr = ptr;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
__destroy_into_raw() {
|
||||
const ptr = this.ptr;
|
||||
this.ptr = 0;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
free() {
|
||||
const ptr = this.__destroy_into_raw();
|
||||
wasm.__wbg_wasminputresult_free(ptr);
|
||||
}
|
||||
/**
|
||||
* @returns {number}
|
||||
*/
|
||||
get tag() {
|
||||
var ret = wasm.__wbg_get_wasminputresult_tag(this.ptr);
|
||||
return ret >>> 0;
|
||||
}
|
||||
/**
|
||||
* @param {number} arg0
|
||||
*/
|
||||
set tag(arg0) {
|
||||
wasm.__wbg_set_wasminputresult_tag(this.ptr, arg0);
|
||||
}
|
||||
/**
|
||||
* @returns {string}
|
||||
*/
|
||||
get msg() {
|
||||
try {
|
||||
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
||||
wasm.wasminputresult_msg(retptr, this.ptr);
|
||||
var r0 = getInt32Memory0()[retptr / 4 + 0];
|
||||
var r1 = getInt32Memory0()[retptr / 4 + 1];
|
||||
return getStringFromWasm0(r0, r1);
|
||||
} finally {
|
||||
wasm.__wbindgen_add_to_stack_pointer(16);
|
||||
wasm.__wbindgen_free(r0, r1);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @returns {any}
|
||||
*/
|
||||
get errors() {
|
||||
var ret = wasm.wasminputresult_errors(this.ptr);
|
||||
return takeObject(ret);
|
||||
}
|
||||
}
|
||||
|
||||
export function __wbindgen_json_parse(arg0, arg1) {
|
||||
var ret = JSON.parse(getStringFromWasm0(arg0, arg1));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
|
||||
export function __wbindgen_object_drop_ref(arg0) {
|
||||
takeObject(arg0);
|
||||
};
|
||||
|
||||
export function __wbindgen_object_clone_ref(arg0) {
|
||||
var ret = getObject(arg0);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
|
||||
export function __wbindgen_string_get(arg0, arg1) {
|
||||
const obj = getObject(arg1);
|
||||
var ret = typeof(obj) === 'string' ? obj : undefined;
|
||||
var ptr0 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
var len0 = WASM_VECTOR_LEN;
|
||||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
};
|
||||
|
||||
export function __wbindgen_throw(arg0, arg1) {
|
||||
throw new Error(getStringFromWasm0(arg0, arg1));
|
||||
};
|
||||
|
21
website/nickel-repl/nickel_bg.wasm.d.ts
vendored
@ -1,21 +0,0 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const memory: WebAssembly.Memory;
|
||||
export function __wbg_wasminitresult_free(a: number): void;
|
||||
export function __wbg_get_wasminitresult_tag(a: number): number;
|
||||
export function __wbg_set_wasminitresult_tag(a: number, b: number): void;
|
||||
export function wasminitresult_msg(a: number, b: number): void;
|
||||
export function wasminitresult_repl(a: number): number;
|
||||
export function __wbg_wasminputresult_free(a: number): void;
|
||||
export function __wbg_get_wasminputresult_tag(a: number): number;
|
||||
export function __wbg_set_wasminputresult_tag(a: number, b: number): void;
|
||||
export function wasminputresult_msg(a: number, b: number): void;
|
||||
export function wasminputresult_errors(a: number): number;
|
||||
export function __wbg_replstate_free(a: number): void;
|
||||
export function repl_init(): number;
|
||||
export function repl_input(a: number, b: number, c: number): number;
|
||||
export function repl_serialize(a: number, b: number, c: number, d: number): number;
|
||||
export function __wbindgen_malloc(a: number): number;
|
||||
export function __wbindgen_realloc(a: number, b: number, c: number): number;
|
||||
export function __wbindgen_add_to_stack_pointer(a: number): number;
|
||||
export function __wbindgen_free(a: number, b: number): void;
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "nickel-repl",
|
||||
"collaborators": [
|
||||
"Nicl team"
|
||||
],
|
||||
"description": "Programmable configuration files.",
|
||||
"version": "0.1.0",
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"files": [
|
||||
"nickel_bg.wasm",
|
||||
"nickel.js",
|
||||
"nickel.d.ts"
|
||||
],
|
||||
"module": "nickel.js",
|
||||
"types": "nickel.d.ts",
|
||||
"sideEffects": false
|
||||
}
|
47479
website/package-lock.json
generated
@ -1,48 +0,0 @@
|
||||
{
|
||||
"name": "nickel-lang.org",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"description": "nickel-lang.org",
|
||||
"author": "Yann Hamdaoui",
|
||||
"keywords": [
|
||||
"gatsby"
|
||||
],
|
||||
"scripts": {
|
||||
"develop": "gatsby develop",
|
||||
"start": "gatsby develop",
|
||||
"build": "gatsby build",
|
||||
"serve": "gatsby serve",
|
||||
"clean": "gatsby clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.35",
|
||||
"@fortawesome/free-brands-svg-icons": "^5.15.3",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.15.3",
|
||||
"@fortawesome/react-fontawesome": "^0.1.14",
|
||||
"@loadable/component": "^5.15.0",
|
||||
"ace-builds": "^1.4.12",
|
||||
"ansi-to-react": "^6.1.5",
|
||||
"bootstrap": "^4.6.0",
|
||||
"create-react-class": "^15.7.0",
|
||||
"gatsby": "^3.2.1",
|
||||
"gatsby-plugin-fontawesome-css": "^1.1.0",
|
||||
"gatsby-plugin-image": "^1.3.0",
|
||||
"gatsby-plugin-react-helmet": "^4.3.0",
|
||||
"gatsby-plugin-sass": "^4.8.0",
|
||||
"gatsby-plugin-sharp": "^3.3.0",
|
||||
"gatsby-remark-prismjs": "^5.0.0",
|
||||
"gatsby-source-filesystem": "^3.3.0",
|
||||
"gatsby-transformer-sharp": "^3.3.0",
|
||||
"jquery": "^3.6.0",
|
||||
"nickel-repl": "file:nickel-repl",
|
||||
"popper.js": "^1.16.1",
|
||||
"prismjs": "^1.23.0",
|
||||
"react": "^17.0.1",
|
||||
"react-ace": "^9.4.0",
|
||||
"react-bootstrap": "^1.5.2",
|
||||
"react-bootstrap-icons": "^1.5.0",
|
||||
"react-dom": "^17.0.1",
|
||||
"react-helmet": "^6.1.0",
|
||||
"sass": "^1.35.1"
|
||||
}
|
||||
}
|
@ -1,158 +0,0 @@
|
||||
/**
|
||||
* Define a Nickel mode in the ace editor. This enables syntax highlighting for the Nickel language.
|
||||
*/
|
||||
|
||||
import ace from "ace-builds/src-noconflict/ace";
|
||||
|
||||
ace.define('ace/mode/nickel_highlight_rules', ['require', 'exports', 'ace/lib/oop', 'ace/mode/text_highlight_rules'], function (_require, exports, _module) {
|
||||
const oop = ace.require("ace/lib/oop");
|
||||
const TextHighlightRules = ace.require("ace/mode/text_highlight_rules").TextHighlightRules;
|
||||
|
||||
const NickelHighlightRules = function () {
|
||||
const constantLanguage = "true|false|null";
|
||||
const keywordControl = "switch|import|if|else|then";
|
||||
const keywordDeclaration = "let|in";
|
||||
const keywordMetavalue = "doc|default";
|
||||
|
||||
const keywordMapper = this.createKeywordMapper({
|
||||
"constant.language.nickel": constantLanguage,
|
||||
"keyword.control.nickel": keywordControl,
|
||||
"keyword.declaration.nickel": keywordDeclaration,
|
||||
'keyword.metavalue.nickel': keywordMetavalue,
|
||||
}, "identifier");
|
||||
|
||||
// Although Ace supports modal lexing (the next, push and pop rules allow to
|
||||
// maintain a state and a stack), we can't encode nickel
|
||||
// variable-length delimiter directly with one nice generic rule.
|
||||
//
|
||||
// We thus generate a rule for lengths 1, 2 and 3 (m#", m##", and m###")
|
||||
// plus write a generic rule for size n. The generic rule is wrong for
|
||||
// length 5 and above, but this is highly unlikely to be used in
|
||||
// practice.
|
||||
|
||||
// Generate the starting rule of a string with variable-length
|
||||
// delimiters
|
||||
let genQqdoc = length => ({
|
||||
token: "string",
|
||||
regex: `m${'#'.repeat(length)}"`,
|
||||
next: `qqdoc${length}`,
|
||||
});
|
||||
|
||||
// Generate the escape and end rules of a string with variable-length delimiters
|
||||
let genQqdocState = length => ({
|
||||
[`qqdoc${length}`]: [
|
||||
{
|
||||
token: "constant.language.escape",
|
||||
regex: `${'#'.repeat(length)}{`,
|
||||
push: "start",
|
||||
}, {
|
||||
token: "string",
|
||||
regex: `"${'#'.repeat(length)}m`,
|
||||
next: "pop",
|
||||
}, {
|
||||
defaultToken: "string"
|
||||
}]
|
||||
});
|
||||
|
||||
this.$rules = {
|
||||
"start": [{
|
||||
token: "comment",
|
||||
regex: /\/\/.*$/
|
||||
}, {
|
||||
regex: "(==|!=|<=?|>=?)",
|
||||
token: ["keyword.operator.comparison.nickel"]
|
||||
}, {
|
||||
regex: "(\\+\\+|@)",
|
||||
token: ["keyword.operator.combinator.nickel"]
|
||||
}, {
|
||||
regex: "(#|->|:)",
|
||||
token: ["keyword.operator.type.nickel"]
|
||||
}, {
|
||||
regex: "=",
|
||||
token: "keyword.operator.assignment.nickel"
|
||||
},
|
||||
{
|
||||
token: "string",
|
||||
regex: "\"",
|
||||
next: "qqstring",
|
||||
},
|
||||
{
|
||||
token: "string",
|
||||
regex: "m(#{4,})\"",
|
||||
next: "qqdocn"
|
||||
},
|
||||
genQqdoc(1),
|
||||
genQqdoc(2),
|
||||
genQqdoc(3), {
|
||||
token: "constant.numeric", // hex
|
||||
regex: "0[xX][0-9a-fA-F]+\\b"
|
||||
}, {
|
||||
token: "constant.numeric", // float
|
||||
regex: "[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"
|
||||
}, {
|
||||
token: keywordMapper,
|
||||
regex: "[a-zA-Z_$][a-zA-Z0-9_$]*\\b"
|
||||
}, {
|
||||
regex: "}",
|
||||
token: function (_val, _start, stack) {
|
||||
return stack[1] && stack[1].charAt(0) === "q" ? "constant.language.escape" : "text";
|
||||
},
|
||||
next: "pop"
|
||||
}],
|
||||
"qqdocn": [
|
||||
{
|
||||
token: "constant.language.escape",
|
||||
regex: "#{4,}{",
|
||||
push: "start"
|
||||
}, {
|
||||
token: "string",
|
||||
regex: "\"(#{4,})m",
|
||||
next: "pop"
|
||||
}, {
|
||||
defaultToken: "string"
|
||||
}],
|
||||
...genQqdocState(1),
|
||||
...genQqdocState(2),
|
||||
...genQqdocState(3),
|
||||
"qqstring": [
|
||||
{
|
||||
token: "constant.language.escape",
|
||||
regex: "#{",
|
||||
push: "start"
|
||||
}, {
|
||||
token: "string",
|
||||
regex: '"',
|
||||
next: "pop"
|
||||
}, {
|
||||
defaultToken: "string"
|
||||
}],
|
||||
};
|
||||
|
||||
this.normalizeRules();
|
||||
};
|
||||
|
||||
oop.inherits(NickelHighlightRules, TextHighlightRules);
|
||||
|
||||
exports.NickelHighlightRules = NickelHighlightRules;
|
||||
});
|
||||
|
||||
ace.define("ace/mode/nickel",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/nickel_highlight_rules","ace/mode/folding/cstyle"], function (_require, exports, _module) {
|
||||
const oop = ace.require("ace/lib/oop");
|
||||
const TextMode = ace.require("ace/mode/text").Mode;
|
||||
const NimHighlightRules = ace.require("ace/mode/nickel_highlight_rules").NickelHighlightRules;
|
||||
|
||||
const Mode = function () {
|
||||
TextMode.call(this);
|
||||
this.HighlightRules = NimHighlightRules;
|
||||
this.$behaviour = this.$defaultBehaviour;
|
||||
};
|
||||
|
||||
oop.inherits(Mode, TextMode);
|
||||
|
||||
(function () {
|
||||
this.lineCommentStart = "//";
|
||||
this.$id = "ace/mode/nickel";
|
||||
}).call(Mode.prototype);
|
||||
|
||||
exports.Mode = Mode;
|
||||
});
|
@ -1,37 +0,0 @@
|
||||
import React from "react";
|
||||
import { Link } from "gatsby";
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import {
|
||||
faGithub,
|
||||
faTwitter
|
||||
} from "@fortawesome/free-brands-svg-icons";
|
||||
import {
|
||||
faArrowUp,
|
||||
faComments
|
||||
} from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
const Footer = () => (
|
||||
<footer className="bg-secondary text-center">
|
||||
<div className="container pt-3">
|
||||
<section>
|
||||
<Link className="btn text-black btn-outline btn-floating m-1" to="https://twitter.com/nickel_lang" role="button"
|
||||
><FontAwesomeIcon color="black" icon={faTwitter}/></Link>
|
||||
|
||||
<Link className="btn btn-outline btn-floating m-1" to="https://github.com/tweag/nickel" role="button"
|
||||
><FontAwesomeIcon color="black" icon={faGithub}/></Link>
|
||||
|
||||
<Link className="btn btn-outline btn-floating m-1" to="https://github.com/tweag/nickel/discussions" role="button"
|
||||
><FontAwesomeIcon color="black" icon={faComments}/></Link>
|
||||
|
||||
<Link className="btn btn-outline-dark btn-floating m-1 ml-4" to="#" role="button"
|
||||
><FontAwesomeIcon color="black" icon={faArrowUp}/></Link>
|
||||
</section>
|
||||
</div>
|
||||
<hr/>
|
||||
<div className="text-center pb-3">
|
||||
© 2021 Copyright: <Link to={'https://github.com/tweag/nickel/graphs/contributors'} className={'link-footer'}>Nickel contributors</Link>
|
||||
</div>
|
||||
</footer>
|
||||
)
|
||||
|
||||
export default Footer
|
@ -1,30 +0,0 @@
|
||||
import React from "react"
|
||||
import { Link } from "gatsby"
|
||||
import {StaticImage} from "gatsby-plugin-image";
|
||||
|
||||
const Header = ({ menuLinks }) => (
|
||||
<header>
|
||||
<nav className="navbar navbar-expand-md navbar-light bg-primary">
|
||||
<div className="container-fluid">
|
||||
<Link className="navbar-brand flex-md-fill w-md-100" to="/">
|
||||
<StaticImage className={"logo-navbar"} src="../images/nickel-logo-2.svg" alt="logo"/><span className="nickel">Nickel</span>
|
||||
</Link>
|
||||
<button className="navbar-toggler" type="button" data-toggle="collapse"
|
||||
data-target="#navbarNavAltMarkup"
|
||||
aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span className="navbar-toggler-icon"/>
|
||||
</button>
|
||||
<div className="collapse navbar-collapse justify-content-center flex-fill w-100" id="navbarNavAltMarkup">
|
||||
<div className="navbar-nav">
|
||||
{menuLinks.map(link => (
|
||||
<Link key={link.name} className="nav-link" activeClassName="active" to={link.link}>{link.name}</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<ul className={'d-none d-md-block w-100 justify-contend-end'}/>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
);
|
||||
|
||||
export default Header
|
@ -1,42 +0,0 @@
|
||||
import React from "react"
|
||||
import {graphql, StaticQuery} from "gatsby"
|
||||
import {Helmet} from "react-helmet"
|
||||
import Header from "./header"
|
||||
import Footer from "./footer"
|
||||
|
||||
export default function Layout({children}) {
|
||||
return (
|
||||
<StaticQuery
|
||||
query={graphql`
|
||||
query SiteTitleQuery {
|
||||
site {
|
||||
siteMetadata {
|
||||
title
|
||||
menuLinks {
|
||||
name
|
||||
link
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`}
|
||||
render={data => (
|
||||
<React.Fragment>
|
||||
<Helmet
|
||||
title={'Nickel'}
|
||||
meta={[
|
||||
{name: 'description', content: 'Sample'},
|
||||
{name: 'keywords', content: 'sample, something'},
|
||||
]}
|
||||
>
|
||||
</Helmet>
|
||||
<Header menuLinks={data.site.siteMetadata.menuLinks}/>
|
||||
<div>
|
||||
{children}
|
||||
</div>
|
||||
<Footer/>
|
||||
</React.Fragment>
|
||||
)}
|
||||
/>
|
||||
)
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
import {lazy} from "@loadable/component";
|
||||
import React from 'react'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import {
|
||||
faSpinner,
|
||||
} from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
/**
|
||||
* Wrapper around the Playground component to use it on the client side only (and not via server-side rendering).
|
||||
* This is made necessary by the code editor, based on react-ace and ace-builds, which use `window` and don't seem to fully support SSR yet.
|
||||
* @type {React.ForwardRefExoticComponent<React.PropsWithoutRef<{}> & React.RefAttributes<unknown>>}
|
||||
*/
|
||||
const LoadablePlayground = lazy(() => import("./playground/playground"));
|
||||
|
||||
export default function ClientSidePlayground(props) {
|
||||
const isSSR = typeof window === "undefined";
|
||||
|
||||
return (
|
||||
<>
|
||||
{!isSSR && (
|
||||
<React.Suspense fallback={
|
||||
<div className={"text-center playground-loader mt-4"}>
|
||||
Loading playground <span className={"ml-2"}><FontAwesomeIcon icon={faSpinner} size={"lg"} spin/></span>
|
||||
</div>
|
||||
}>
|
||||
<LoadablePlayground {...props}/>
|
||||
</React.Suspense>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
@ -1,184 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import AceEditor from 'react-ace';
|
||||
import nickelCodes from './nickel-codes'
|
||||
import {PLAYGROUND_SEND_EVENT, EDITOR_SEND_EVENT, REPL_RUN_EVENT} from "./events";
|
||||
|
||||
import "ace-builds/src-noconflict/theme-solarized_dark";
|
||||
import "../../ace-nickel-mode/ace-nickel-mode";
|
||||
import ReactDOMServer from "react-dom/server";
|
||||
import {wrapPageElement} from "gatsby/dist/utils/api-browser-docs";
|
||||
import modes from "./modes";
|
||||
|
||||
/**
|
||||
* Nickel code editor component, based on the Ace editor.
|
||||
*/
|
||||
export default class Editor extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
value: this.props.value,
|
||||
name: this.props.name,
|
||||
placeholder: 'Write your code. Press Ctrl+Enter (Cmd+Enter) to run it',
|
||||
theme: "solarized_dark",
|
||||
mode: "nickel",
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
enableBasicAutocompletion: false,
|
||||
enableLiveAutocompletion: false,
|
||||
fontSize: 14,
|
||||
showGutter: true,
|
||||
showPrintMargin: true,
|
||||
highlightActiveLine: true,
|
||||
enableSnippets: false,
|
||||
showLineNumbers: true,
|
||||
annotations: [],
|
||||
wrapEnabled: true,
|
||||
};
|
||||
|
||||
if(this.props.fit && this.props.fit === 'code') {
|
||||
// Taking more lines to account for potential wrapping
|
||||
const lines = this.props.value.split(/\r?\n/g).length+2;
|
||||
this.state.maxLines = lines;
|
||||
this.state.minLines = lines;
|
||||
}
|
||||
else if(this.props.fit && this.props.fit === 'lines' && this.props.lines) {
|
||||
this.state.maxLines = this.props.lines;
|
||||
this.state.minLines = this.props.lines;
|
||||
}
|
||||
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.onREPLRun = this.onREPLRun.bind(this);
|
||||
this.send = this.send.bind(this);
|
||||
this.aceEditorRef = React.createRef();
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
value: `let data = {value = "Hello," ++ " world!"} in data.value`,
|
||||
name: 'nickel-repl-input',
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
// Listen to the REPL's execution events, in order to update potential error messages.
|
||||
document.addEventListener(REPL_RUN_EVENT, this.onREPLRun);
|
||||
document.addEventListener(PLAYGROUND_SEND_EVENT, this.send);
|
||||
|
||||
if(this.props.onResize) {
|
||||
const ro = new ResizeObserver(entries => {
|
||||
for(let entry of entries) {
|
||||
this.props.onResize(entry.target.clientHeight);
|
||||
}
|
||||
});
|
||||
ro.observe(document.getElementById(this.state.name));
|
||||
}
|
||||
}
|
||||
|
||||
onChange(newValue) {
|
||||
this.setState({
|
||||
value: newValue
|
||||
});
|
||||
}
|
||||
|
||||
getHeight() {
|
||||
return document.getElementById(this.state.name).clientHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static component displaying a Nickel diagnostic error.
|
||||
* @param diagnostic
|
||||
* @param label
|
||||
* @returns {*}
|
||||
*/
|
||||
annotationWidget(diagnostic, label) {
|
||||
const labelClass = label.style === nickelCodes.error.label.PRIMARY ? 'ansi-red-fg' : 'ansi-blue-fg';
|
||||
return (<div>
|
||||
<span className={"ansi-bright-red-fg"}>{diagnostic.msg}</span><br/>
|
||||
<span className={labelClass}>{label.msg}</span><br/>
|
||||
<ul>
|
||||
{diagnostic.notes.map(note => <li>{note}</li>)}
|
||||
</ul>
|
||||
</div>)
|
||||
}
|
||||
|
||||
/**
|
||||
* Once the REPL has run, update the error messages.
|
||||
* @param result
|
||||
*/
|
||||
onREPLRun({detail: result}) {
|
||||
if (result.tag === nickelCodes.result.ERROR) {
|
||||
const annotations = result.errors.filter(diagnostic => diagnostic.severity >= nickelCodes.error.severity.WARNING)
|
||||
.map(diagnostic => (
|
||||
diagnostic.labels.map(label => ({
|
||||
row: label.line_start,
|
||||
column: label.col_start,
|
||||
html: ReactDOMServer.renderToStaticMarkup(this.annotationWidget(diagnostic, label)),
|
||||
type: label.style === nickelCodes.error.label.PRIMARY ? 'error' : 'warning',
|
||||
}))
|
||||
)).flat();
|
||||
|
||||
// In some obscure circumstances (annotation on the last line, and then insertion of a new line), annotations disappear, even if the user send the same input again.
|
||||
// To avoid this and make annotations reappear at least when sending an input, we clear the old one first, to triggers reactive updates.
|
||||
this.setState({annotations: []}, () => this.setState({annotations}));
|
||||
} else {
|
||||
this.setState({annotations: []});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch an EDITOR_SEND_EVENT with the current content as a payload.
|
||||
*/
|
||||
send() {
|
||||
// Dispatch the result as an event, so that the editor or other components can react to the outcome of the last input
|
||||
const event = new CustomEvent(EDITOR_SEND_EVENT, {detail: this.state.value});
|
||||
document.dispatchEvent(event);
|
||||
}
|
||||
|
||||
render() {
|
||||
const setEditorMargin = (editor) => {
|
||||
editor.renderer.setPadding(10);
|
||||
editor.renderer.setScrollMargin(10, 10, 0, 0);
|
||||
};
|
||||
|
||||
return <AceEditor
|
||||
ref={this.aceEditorRef}
|
||||
placeholder={this.state.placeholder}
|
||||
mode={this.state.mode}
|
||||
theme={this.state.theme}
|
||||
name={this.state.name}
|
||||
height={this.state.height}
|
||||
width={this.state.width}
|
||||
minLines={this.state.minLines}
|
||||
maxLines={this.state.maxLines}
|
||||
onChange={this.onChange}
|
||||
onSelectionChange={this.onSelectionChange}
|
||||
onCursorChange={this.onCursorChange}
|
||||
onValidate={this.onValidate}
|
||||
value={this.state.value}
|
||||
annotations={this.state.annotations}
|
||||
fontSize={this.state.fontSize}
|
||||
showPrintMargin={this.state.showPrintMargin}
|
||||
showGutter={this.state.showGutter}
|
||||
highlightActiveLine={this.state.highlightActiveLine}
|
||||
wrapEnabled={this.state.wrapEnabled}
|
||||
onLoad={setEditorMargin}
|
||||
commands={[
|
||||
{
|
||||
name: 'send-repl',
|
||||
bindKey: {
|
||||
win: 'Ctrl-enter',
|
||||
mac: 'Cmd-enter',
|
||||
},
|
||||
exec: this.send,
|
||||
},
|
||||
]}
|
||||
setOptions={{
|
||||
useWorker: false,
|
||||
enableBasicAutocompletion: this.state.enableBasicAutocompletion,
|
||||
enableLiveAutocompletion: this.state.enableLiveAutocompletion,
|
||||
enableSnippets: this.state.enableSnippets,
|
||||
showLineNumbers: this.state.showLineNumbers,
|
||||
tabSize: 2
|
||||
}}
|
||||
/>;
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
/**
|
||||
* The user request the execution of the current snippet (by clicking the "Run" button, or using the corresponding shortcut).
|
||||
* @type {string}
|
||||
*/
|
||||
const PLAYGROUND_SEND_EVENT = 'playground:send';
|
||||
/**
|
||||
* The editor is sending a snippet for execution (by clicking the "Run" button, or usign the corresponding shortcut). Editor listens to `PLAYGROUND_SEND_EVENT`,
|
||||
* and dispatch `EDITOR_SEND_ENVENT`. The payload (field `detail` of the event) contains the input as a string.
|
||||
* @type {string}
|
||||
*/
|
||||
const EDITOR_SEND_EVENT = 'nickel-repl:send';
|
||||
/**
|
||||
* The REPL has run a snippet and returned. The `details`
|
||||
* @type {string}
|
||||
*/
|
||||
const REPL_RUN_EVENT = 'nickel-repl:run';
|
||||
|
||||
export {PLAYGROUND_SEND_EVENT, EDITOR_SEND_EVENT, REPL_RUN_EVENT};
|
@ -1,12 +0,0 @@
|
||||
/**
|
||||
* The different execution modes of the REPL component.
|
||||
* @type {{REPL: string, JSON: string, TOML: string, YAML: string}}
|
||||
*/
|
||||
const modes = {
|
||||
REPL: 'repl',
|
||||
JSON: 'json',
|
||||
TOML: 'toml',
|
||||
YAML: 'yaml',
|
||||
};
|
||||
|
||||
export default modes;
|
@ -1,27 +0,0 @@
|
||||
/**
|
||||
* Codes returned by the Nickel WASM evaluator.
|
||||
* @type {{result: {BLANK: number, SUCCESS: number, PARTIAL: number, ERROR: number}, error: {severity: {HELP: number, BUG: number, NOTE: number, ERROR: number, WARNING: number}, label: {SECONDARY: number, PRIMARY: number}}}}
|
||||
*/
|
||||
const nickelCodes = {
|
||||
result: {
|
||||
SUCCESS: 0,
|
||||
BLANK: 1,
|
||||
PARTIAL: 2,
|
||||
ERROR: 3,
|
||||
},
|
||||
error: {
|
||||
severity: {
|
||||
HELP: 1,
|
||||
NOTE: 2,
|
||||
WARNING: 3,
|
||||
ERROR: 4,
|
||||
BUG: 5,
|
||||
},
|
||||
label: {
|
||||
PRIMARY: 0,
|
||||
SECONDARY: 1,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default nickelCodes;
|
@ -1,98 +0,0 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import Editor from "./editor";
|
||||
import Repl from "./repl";
|
||||
import modes from "./modes";
|
||||
import {PLAYGROUND_SEND_EVENT} from "./events.js";
|
||||
import {Command} from 'react-bootstrap-icons';
|
||||
|
||||
/**
|
||||
* Playground component, composed of both a code editor and a REPL component.
|
||||
*/
|
||||
export default class Playground extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {mode: this.props.mode};
|
||||
this.setMode = this.setMode.bind(this);
|
||||
this.dispatchSendEvent = this.dispatchSendEvent.bind(this);
|
||||
|
||||
if(this.props.fit === 'code' || this.props.fit === 'lines') {
|
||||
// In fit-to-code mode, fix the height of the output (terminal) element to the height of the current code
|
||||
this.onEditorResize = (height) => {
|
||||
if(this.terminalContainer) {
|
||||
this.terminalContainer.style.height = height + "px";
|
||||
}
|
||||
else {
|
||||
setTimeout(this.onEditorResize, 100)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// If a program was provided initially, run it.
|
||||
if(this.props.value) {
|
||||
this.editor.send();
|
||||
}
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
mode: modes.REPL,
|
||||
fit: 'page',
|
||||
};
|
||||
|
||||
setTerminalContainer = element => this.terminalContainer = element;
|
||||
setEditor = editor => this.editor = editor;
|
||||
|
||||
replTabStyle = (mode) => ('nav-link link-secondary playground-nav-item' + (this.state.mode === mode ? ' active' : ''));
|
||||
|
||||
setMode = (mode) => {
|
||||
this.setState({mode});
|
||||
};
|
||||
|
||||
dispatchSendEvent = () => {
|
||||
const event = new CustomEvent(PLAYGROUND_SEND_EVENT);
|
||||
document.dispatchEvent(event);
|
||||
};
|
||||
|
||||
render() {
|
||||
return <React.Fragment>
|
||||
<div className={"row"}>
|
||||
<div className={"col-6 playground-tab d-flex align-items-center"}>
|
||||
<div>
|
||||
<button className={"btn btn-primary"} onClick={() => this.dispatchSendEvent()}>Run</button>
|
||||
<span className={'ml-4'}> or press</span> <kbd>Ctrl</kbd>+<kbd>Enter</kbd> or <kbd>Cmd <Command/>
|
||||
</kbd>+<kbd>Enter</kbd>
|
||||
</div>
|
||||
</div>
|
||||
<ul className={"col-6 nav nav-pills playground-tab"}>
|
||||
<li className="nav-item">
|
||||
<a className={this.replTabStyle(modes.REPL)} onClick={() => this.setMode(modes.REPL)}>REPL</a>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
<a className={this.replTabStyle(modes.JSON)} onClick={() => this.setMode(modes.JSON)}>JSON</a>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
<a className={this.replTabStyle(modes.YAML)} onClick={() => this.setMode(modes.YAML)}>YAML</a>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
<a className={this.replTabStyle(modes.TOML)} onClick={() => this.setMode(modes.TOML)}>TOML</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<section className={'row playground-container overflow-hidden flex-grow-1'}>
|
||||
<div className={'col-6'}>
|
||||
<Editor ref={this.setEditor} fit={this.props.fit} lines={this.props.lines} value={this.props.value} onResize={this.onEditorResize}/>
|
||||
</div>
|
||||
<div id={"playground-terminal-container"}
|
||||
ref={this.setTerminalContainer}
|
||||
className={'col-6 ansi-monokai playground-terminal-container'}>
|
||||
<Repl containerId={"playground-terminal-container"} className={'playground-terminal'}
|
||||
mode={this.state.mode}/>
|
||||
</div>
|
||||
</section>
|
||||
</React.Fragment>
|
||||
}
|
||||
}
|
@ -1,179 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import {repl_init, repl_input, repl_serialize} from "nickel-repl";
|
||||
import Ansi from "ansi-to-react";
|
||||
import {EDITOR_SEND_EVENT, REPL_RUN_EVENT} from "./events";
|
||||
import modes from './modes';
|
||||
import nickelCodes from './nickel-codes';
|
||||
|
||||
/**
|
||||
* An REPL. This component can run Nickel programs or REPL commands and display a stylized output.
|
||||
*/
|
||||
export default class Repl extends React.Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.state = {
|
||||
lastInput: '',
|
||||
// Output displayed in REPL mode. It is appended to at each run.
|
||||
output_repl: "Nickel Online REPL | Welcome to the Nickel online REPL.\n"
|
||||
+ "See the output of your snippets here.\n\n",
|
||||
// Output displayed in serialize mode. Cleared at each new run.
|
||||
output_serialize: '',
|
||||
};
|
||||
this.endRef = React.createRef();
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
mode: modes.REPL
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the current REPL output as an array of lines.
|
||||
* @returns {string[]}
|
||||
*/
|
||||
lines() {
|
||||
return this.state.output_repl.split(/\r?\n/g);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const result = repl_init();
|
||||
|
||||
if (result.tag === nickelCodes.result.ERROR) {
|
||||
this.write(`Initialization error: ${result.msg}\n`);
|
||||
} else {
|
||||
// /!\ WARNING: result is moved by the Rust code when calling to the repl() method. Do not use or copy result after this call to repl().
|
||||
this.repl = result.repl();
|
||||
this.prompt();
|
||||
}
|
||||
document.addEventListener(EDITOR_SEND_EVENT, this.onSend.bind(this))
|
||||
}
|
||||
|
||||
/**
|
||||
* Write text. Newlines and ANSI escape codes are converted to HTML before rendering.
|
||||
* In serialize mode, the new output erase the old content. In REPL mode, the new output is appended to.
|
||||
* Because state updates are asynchronous, this returns a Promise that resolves when everything is up to date.
|
||||
* @param data String
|
||||
* @returns {Promise<unknown>}
|
||||
*/
|
||||
write(data) {
|
||||
return new Promise(resolve => {
|
||||
if (this.props.mode === modes.REPL) {
|
||||
this.setState({output_repl: this.state.output_repl + data}, resolve);
|
||||
} else {
|
||||
this.setState({output_serialize: data}, resolve);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the output.
|
||||
* @returns {Promise<unknown>}
|
||||
*/
|
||||
clear() {
|
||||
return new Promise(resolve => {
|
||||
if (this.props.mode === modes.REPL) {
|
||||
this.setState({output_repl: ''}, resolve);
|
||||
} else {
|
||||
this.setState({output_serialize: ''}, resolve);
|
||||
}
|
||||
}
|
||||
).then(() => this.prompt());
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a new line followed by a prompt, if in REPL mode. Do nothing otherwise.
|
||||
* @returns {Promise<unknown>}
|
||||
*/
|
||||
prompt = () => {
|
||||
if (this.props.mode === modes.REPL) {
|
||||
return this.write('\n\u001b[32mnickel>\u001b[0m ');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Run it. In REPL mode, the input is also appended to the output.
|
||||
* @param input String
|
||||
*/
|
||||
onSend = ({detail: input}) => {
|
||||
if (this.props.mode === modes.REPL) {
|
||||
return this.write(input).then(() => this.run(input));
|
||||
} else {
|
||||
return this.run(input);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Run an input and write the result in the output.
|
||||
* @param input String
|
||||
* @returns {Promise<number>} A promise resolving to the return code of the execution of the Nickel REPL, or -1 if the REPL wasn't loaded.
|
||||
*/
|
||||
run = (input) => {
|
||||
if (this.repl === null) {
|
||||
console.error("Terminal: REPL is not loaded (this.repl === null)");
|
||||
return new Promise(resolve => resolve(-1));
|
||||
}
|
||||
|
||||
this.setState({lastInput: input});
|
||||
|
||||
let result;
|
||||
|
||||
if (this.props.mode !== modes.REPL) {
|
||||
result = repl_serialize(this.repl, this.props.mode, input);
|
||||
}
|
||||
else {
|
||||
result = repl_input(this.repl, input);
|
||||
}
|
||||
|
||||
let task;
|
||||
|
||||
if (this.props.mode === modes.REPL) {
|
||||
task = this.write("\n" + result.msg).then(() => this.prompt());
|
||||
} else {
|
||||
//If there's an error, we run the original snippet in order to have a better error message.
|
||||
if (result.tag === nickelCodes.result.ERROR) {
|
||||
const resultAlone = repl_input(this.repl, this.state.lastInput);
|
||||
|
||||
// If there's no error for the original snippet alone, this may be a NonSerializable error. In this case,
|
||||
// we keep the first error message.
|
||||
if (resultAlone.tag === nickelCodes.result.ERROR) {
|
||||
result = resultAlone;
|
||||
}
|
||||
}
|
||||
|
||||
task = this.write(result.msg);
|
||||
}
|
||||
|
||||
// Dispatch the result as an event, so that the editor or other components can react to the outcome of the last input
|
||||
const event = new CustomEvent(REPL_RUN_EVENT, {detail: result});
|
||||
document.dispatchEvent(event);
|
||||
|
||||
return task.then(() => result.tag);
|
||||
};
|
||||
|
||||
componentDidUpdate = (prevProps) => {
|
||||
// If we switched mode to a serialization mode and there is a last input, we re-run the last input
|
||||
if (this.props.mode !== prevProps.mode && this.props.mode !== modes.REPL && this.state.lastInput !== '') {
|
||||
this.run(this.state.lastInput);
|
||||
}
|
||||
|
||||
// Scroll to the last message
|
||||
const terminalContainer = document.getElementById(this.props.containerId);
|
||||
terminalContainer.scrollTop = terminalContainer.scrollHeight;
|
||||
};
|
||||
|
||||
render() {
|
||||
let content;
|
||||
|
||||
if (this.props.mode === modes.REPL) {
|
||||
content = this.lines().map((line, index) => <div key={index}><Ansi useClasses>{line.toString()}</Ansi><br/>
|
||||
</div>);
|
||||
} else {
|
||||
content = <Ansi useClasses>{this.state.output_serialize}</Ansi>;
|
||||
}
|
||||
|
||||
return <div style={{whiteSpace: 'pre-wrap'}}>
|
||||
{content}
|
||||
<div ref={this.endRef}/>
|
||||
</div>
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 9.7 KiB |
Before Width: | Height: | Size: 10 KiB |
@ -1 +0,0 @@
|
||||
<svg id="Calque_1" data-name="Calque 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 299.83 339.82"><defs><style>.cls-1{fill:#e0c3fc;}.cls-2{fill:#fff;}</style></defs><path class="cls-1" d="M1110.37,614.62V465.38a20.67,20.67,0,0,0-10.34-17.9L970.79,372.86a20.68,20.68,0,0,0-20.67,0L820.88,447.48a20.67,20.67,0,0,0-10.34,17.9V614.62a20.67,20.67,0,0,0,10.34,17.9l129.24,74.62a20.68,20.68,0,0,0,20.67,0L1100,632.52A20.67,20.67,0,0,0,1110.37,614.62Z" transform="translate(-810.54 -370.09)"/><path class="cls-2" d="M1023.14,509.09V511q0,34.5,0,69a2.31,2.31,0,0,1-1.57,2.53q-25.8,11.37-51.54,22.85c-.44.2-.89.37-1.5.62,0-.49-.1-.88-.1-1.28q0-35,0-70a1.77,1.77,0,0,1,1.2-2q26.14-11.55,52.27-23.18C1022.24,509.45,1022.59,509.32,1023.14,509.09Z" transform="translate(-810.54 -370.09)"/><path class="cls-2" d="M898.14,509.06l8.94,3.94q22,9.79,44.11,19.56a2.38,2.38,0,0,1,1.67,2.64q-.08,34.41,0,68.83v2c-.62-.24-1.11-.41-1.59-.62q-25.83-11.48-51.68-22.92a2.09,2.09,0,0,1-1.45-2.28q.06-34.66,0-69.33Z" transform="translate(-810.54 -370.09)"/><path class="cls-2" d="M1011.61,497.07l-23.47,10.42c-8.76,3.89-17.52,7.8-26.3,11.65a3.1,3.1,0,0,1-2.24.06q-24.45-10.77-48.86-21.66c-.25-.1-.48-.25-.91-.47a11.69,11.69,0,0,1,1.11-.67q24.27-10.78,48.56-21.54a3,3,0,0,1,2.1-.07q24.52,10.83,49,21.74C1010.84,496.63,1011.06,496.77,1011.61,497.07Z" transform="translate(-810.54 -370.09)"/></svg>
|
Before Width: | Height: | Size: 1.3 KiB |
@ -1 +0,0 @@
|
||||
<svg id="Calque_1" data-name="Calque 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 299.09 338.96"><defs><style>.cls-1{fill:#3a3a3c;}.cls-2{fill:#e0c3fc;}</style></defs><path class="cls-1" d="M1110,614.41V465.59a20.68,20.68,0,0,0-10.34-17.9l-128.87-74.4a20.63,20.63,0,0,0-20.67,0l-128.88,74.4a20.69,20.69,0,0,0-10.33,17.9V614.41a20.69,20.69,0,0,0,10.33,17.9l128.88,74.4a20.63,20.63,0,0,0,20.67,0l128.87-74.4A20.68,20.68,0,0,0,1110,614.41Z" transform="translate(-810.91 -370.52)"/><path class="cls-2" d="M1023,509.16v1.9q0,34.41,0,68.83a2.31,2.31,0,0,1-1.56,2.53Q995.71,593.76,970,605.21c-.44.2-.89.37-1.5.62,0-.49-.1-.88-.1-1.28q0-34.9,0-69.8a1.77,1.77,0,0,1,1.19-2q26.1-11.52,52.15-23.12Z" transform="translate(-810.91 -370.52)"/><path class="cls-2" d="M898.29,509.14c3.12,1.37,6,2.64,8.92,3.93,14.67,6.51,29.32,13,44,19.51a2.39,2.39,0,0,1,1.66,2.63q-.07,34.34,0,68.67v2c-.62-.24-1.11-.41-1.58-.62q-25.77-11.44-51.56-22.87a2.07,2.07,0,0,1-1.44-2.27q0-34.58,0-69.15Z" transform="translate(-810.91 -370.52)"/><path class="cls-2" d="M1011.49,497.17l-23.42,10.4c-8.74,3.88-17.47,7.79-26.23,11.62a3.15,3.15,0,0,1-2.24.07q-24.39-10.75-48.74-21.61c-.25-.11-.48-.25-.9-.48.44-.26.75-.5,1.1-.66q24.21-10.77,48.45-21.49a3,3,0,0,1,2.09-.07q24.46,10.8,48.89,21.69C1010.72,496.74,1010.94,496.88,1011.49,497.17Z" transform="translate(-810.91 -370.52)"/></svg>
|
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 7.9 KiB |
Before Width: | Height: | Size: 8.2 KiB |
Before Width: | Height: | Size: 5.6 KiB |
@ -1,174 +0,0 @@
|
||||
---
|
||||
page: "getting-started"
|
||||
---
|
||||
|
||||
# Getting started
|
||||
|
||||
Nickel is quite new and not yet distributed using the standard channels
|
||||
(binaries, nix package, Rust crate, and so on). We are sorry if the installation
|
||||
process is not yet optimal, but this should change soon, so stay tuned.
|
||||
|
||||
## Build from source using Nix
|
||||
|
||||
Using [Nix]("https://nixos.org/") is the easiest way to get a Nickel executable
|
||||
running:
|
||||
|
||||
1. Clone the [Nickel repository](https://github.io/tweag/nickel)
|
||||
locally and set it as the current directory:
|
||||
|
||||
```shell-session
|
||||
$ git clone git@github.com:tweag/nickel.git
|
||||
Cloning in 'nickel'...
|
||||
[..]
|
||||
$ cd nickel
|
||||
devops@nickel-lang:~/nickel$
|
||||
```
|
||||
|
||||
1. Invoke nix build:
|
||||
|
||||
```shell-session
|
||||
devops@nickel-lang:~/nickel$ nix build
|
||||
[1 built, 0.0 MiB DL]
|
||||
devops@nickel-lang:~/nickel$
|
||||
```
|
||||
|
||||
1. If everything went right, a binary is now available in the
|
||||
result directory:
|
||||
|
||||
```shell-session
|
||||
devops@nickel-lang:~/nickel$ ./result/bin/nickel -V
|
||||
nickel 0.1.0
|
||||
devops@nickel-lang:~/nickel$
|
||||
```
|
||||
|
||||
## Build from source without Nix
|
||||
|
||||
You will find alternative ways to build Nickel from source by cloning the
|
||||
[repository](href="https://github.io/tweag/nickel) and following the
|
||||
instructions of the
|
||||
[README](href="https://github.com/tweag/nickel/#getting-started").
|
||||
|
||||
## Write your first configuration
|
||||
|
||||
Nickel has a ton of cool features, like gradual typing, contracts and a merge
|
||||
system. However, you'll only have to deal with them once you need them. Writing
|
||||
basic configuration is almost as writing JSON or YAML. Let us start with a
|
||||
basic fictional app configuration:
|
||||
|
||||
```nickel
|
||||
{
|
||||
name = "example",
|
||||
description = m#"
|
||||
This is an awesome software I'm developing.
|
||||
Please use it!
|
||||
"#m,
|
||||
version = "0.1.1",
|
||||
main = "index.js",
|
||||
keywords = ["example", "config"],
|
||||
scripts = {
|
||||
test = m#"test.sh --option --install example --version "0.1.1""#m,
|
||||
do_stuff = "do_stuff.sh subcommand",
|
||||
},
|
||||
contributors = [{
|
||||
name = "John Doe",
|
||||
email = "johndoe@example.com"
|
||||
}, {
|
||||
name = "Ivy Lane",
|
||||
url = "https=//example.com/ivylane"
|
||||
}],
|
||||
dependencies = {
|
||||
dep1 = "^1.0.0",
|
||||
dep3 = "6.7"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This program describe a record delimited by `{` and `}`, consisting in a list of
|
||||
key-value pairs, akin to JSON's objects. Nickel basic datatypes include strings
|
||||
delimited by `"` and lists, by `[` and `]`.
|
||||
|
||||
The m#" and "#m delimits multiline strings. In such strings, the common
|
||||
indentation prefix is stripped, and special characters (excepted
|
||||
interpolation #{}) loose their meaning. It is useful for two purpose
|
||||
illustrated here:
|
||||
|
||||
- Writing strings spanning multiple lines while keeping the same
|
||||
indentation as code.
|
||||
- Writing strings with special characters in it, without having to
|
||||
escape them (", \, and so on).
|
||||
|
||||
## Export
|
||||
|
||||
Now, save the content in "example.ncl" and run nickel export (or
|
||||
./result/bin/nickel export if you haven't made a symbolic link):
|
||||
|
||||
```shell-session
|
||||
devops@nickel-lang:~/nickel$ nickel -f example.ncl export --format yaml
|
||||
---
|
||||
contributors:
|
||||
- email: johndoe@example.com
|
||||
name: John Doe
|
||||
- name: Ivy Lane
|
||||
url: https=//example.com/ivylane
|
||||
dependencies:
|
||||
dep1: ^1.0.0
|
||||
dep3: "6.7"
|
||||
description: "This is an awesome software I'm developing.\nPlease use it!"
|
||||
keywords:
|
||||
- example
|
||||
- config
|
||||
main: index.js
|
||||
name: example
|
||||
scripts:
|
||||
do_stuff: do_stuff.sh subcommand
|
||||
test: "test.sh --option --install example --version \"0.1.1\""
|
||||
version: 0.1.1
|
||||
```
|
||||
|
||||
Currently supported formats are yaml, toml, json, and raw. json is the
|
||||
default, while raw expect a string result that it output directly, useful to
|
||||
generate e.g. shell scripts or other custom data.
|
||||
|
||||
## Reuse
|
||||
|
||||
Nickel is a programming language. This allows you not only to describe, but to
|
||||
generate data. There's some repetition in our previous example (reproducing only
|
||||
the interesting part):
|
||||
|
||||
```nickel
|
||||
name = "example",
|
||||
version = "0.1.1",
|
||||
scripts = {
|
||||
test = m#"test.sh --option --install example --version "0.1.1""#m,
|
||||
```
|
||||
|
||||
Apart from aesthetics, a more serious issue is inconsistency. If you bump the
|
||||
version number in version, you may forget to do so in the test scripts as well,
|
||||
leading to an incorrect configuration. To remedy this problem, let us have a
|
||||
single source of truth by reusing the value of name and version in test, using
|
||||
the interpolation syntax `#{expr}`:
|
||||
|
||||
```nickel
|
||||
name = "example",
|
||||
version = "0.1.1",
|
||||
scripts = {
|
||||
test = m#"test.sh --option --install #{name} --version "#{version}""#m
|
||||
```
|
||||
|
||||
Now, if we change version to "0.1.2" and export the result, the test script
|
||||
invocation is updated as well:
|
||||
|
||||
```yaml
|
||||
# [...]
|
||||
scripts:
|
||||
do_stuff: do_stuff.sh subcommand
|
||||
test: "test.sh --option --install example --version \"0.1.2\""
|
||||
version: 0.1.2
|
||||
```
|
||||
|
||||
## Going further
|
||||
|
||||
This was a short introduction that should get you started. But Nickel is a
|
||||
full-fledged programming language, featuring higher-order functions, gradual
|
||||
typing, contracts, and much more. You'll find more resources on the
|
||||
[Documentation](/documentation) page.
|
@ -1,54 +0,0 @@
|
||||
import * as React from "react"
|
||||
import { Link } from "gatsby"
|
||||
|
||||
// styles
|
||||
const pageStyles = {
|
||||
color: "#232129",
|
||||
padding: "96px",
|
||||
fontFamily: "-apple-system, Roboto, sans-serif, serif",
|
||||
}
|
||||
const headingStyles = {
|
||||
marginTop: 0,
|
||||
marginBottom: 64,
|
||||
maxWidth: 320,
|
||||
}
|
||||
|
||||
const paragraphStyles = {
|
||||
marginBottom: 48,
|
||||
}
|
||||
const codeStyles = {
|
||||
color: "#8A6534",
|
||||
padding: 4,
|
||||
backgroundColor: "#FFF4DB",
|
||||
fontSize: "1.25rem",
|
||||
borderRadius: 4,
|
||||
}
|
||||
|
||||
// markup
|
||||
const NotFoundPage = () => {
|
||||
return (
|
||||
<main style={pageStyles}>
|
||||
<title>Not found</title>
|
||||
<h1 style={headingStyles}>Page not found</h1>
|
||||
<p style={paragraphStyles}>
|
||||
Sorry{" "}
|
||||
<span role="img" aria-label="Pensive emoji">
|
||||
😔
|
||||
</span>{" "}
|
||||
we couldn’t find what you were looking for.
|
||||
<br />
|
||||
{process.env.NODE_ENV === "development" ? (
|
||||
<>
|
||||
<br />
|
||||
Try creating a page in <code style={codeStyles}>src/pages/</code>.
|
||||
<br />
|
||||
</>
|
||||
) : null}
|
||||
<br />
|
||||
<Link to="/">Go home</Link>.
|
||||
</p>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
export default NotFoundPage
|
@ -1,30 +0,0 @@
|
||||
import * as React from "react"
|
||||
import Layout from "../components/layout"
|
||||
import { Link } from "gatsby"
|
||||
|
||||
const IndexPage = () => {
|
||||
return (
|
||||
<Layout>
|
||||
<main className="container main-container">
|
||||
<div className="row">
|
||||
<h1 className="main-title col-12">Documentation</h1>
|
||||
|
||||
<div className="mt-4 col-12">
|
||||
<div className="list-group">
|
||||
<Link to="https://github.com/tweag/nickel/#readme" className="list-group-item list-group-item-action">The
|
||||
Nickel README</Link>
|
||||
<Link to="https://github.com/tweag/nickel/blob/master/RATIONALE.md"
|
||||
className="list-group-item list-group-item-action">Design rationale</Link>
|
||||
<Link to="#" className="list-group-item list-group-item-action
|
||||
disabled">Tutorials<span className={'text-primary'}> - coming soon</span></Link>
|
||||
<Link to="#" className="list-group-item list-group-item-action disabled">The Nickel Manual <span className={'text-primary'}> - coming soon</span></Link>
|
||||
<Link to="#" className="list-group-item list-group-item-action disabled">Language specification<span className={'text-primary'}> - coming soon</span></Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</Layout>
|
||||
)
|
||||
};
|
||||
|
||||
export default IndexPage
|
@ -1,211 +0,0 @@
|
||||
import * as React from "react"
|
||||
import {useEffect} from "react"
|
||||
import Layout from "../components/layout"
|
||||
|
||||
import Prism from "prismjs";
|
||||
import "prismjs/components/prism-bash";
|
||||
import "prismjs/components/prism-yaml";
|
||||
import "prismjs/themes/prism-tomorrow.css";
|
||||
import "prismjs/plugins/command-line/prism-command-line";
|
||||
import "prismjs/plugins/command-line/prism-command-line.css";
|
||||
import Playground from "../components/playground-clientside";
|
||||
import modes from "../components/playground/modes";
|
||||
import nickelLanguageDefinition from "../prism/nickel";
|
||||
|
||||
// Escaping curly braces and other stuff in JSX is tiring, so we define all code examples here
|
||||
const codeExamples = {
|
||||
withNix: {
|
||||
clone: `git clone git@github.com:tweag/nickel.git
|
||||
Cloning in 'nickel'...
|
||||
[..]
|
||||
cd nickel`,
|
||||
build: `nix-build
|
||||
[1 built, 0.0 MiB DL]`,
|
||||
run: `./result/bin/nickel -V
|
||||
nickel 0.1.0`,
|
||||
},
|
||||
firstConfig: `{
|
||||
name = "example",
|
||||
description = m#"
|
||||
This is an awesome software I'm developing.
|
||||
Please use it!
|
||||
"#m,
|
||||
version = "0.1.1",
|
||||
main = "index.js",
|
||||
keywords = ["example", "config"],
|
||||
scripts = {
|
||||
test = m#"test.sh --option --install example --version "0.1.1""#m,
|
||||
do_stuff = "do_stuff.sh subcommand",
|
||||
},
|
||||
contributors = [{
|
||||
name = "John Doe",
|
||||
email = "johndoe@example.com"
|
||||
}, {
|
||||
name = "Ivy Lane",
|
||||
url = "https://example.com/ivylane"
|
||||
}],
|
||||
dependencies = {
|
||||
dep1 = "^1.0.0",
|
||||
dep3 = "6.7"
|
||||
}
|
||||
}`,
|
||||
export: `./result/bin/nickel -f example.ncl export --format yaml
|
||||
---
|
||||
contributors:
|
||||
- email: johndoe@example.com
|
||||
name: John Doe
|
||||
- name: Ivy Lane
|
||||
url: https://example.com/ivylane
|
||||
dependencies:
|
||||
dep1: ^1.0.0
|
||||
dep3: "6.7"
|
||||
description: "This is an awesome software I'm developing.\\nPlease use it!"
|
||||
keywords:
|
||||
- example
|
||||
- config
|
||||
main: index.js
|
||||
name: example
|
||||
scripts:
|
||||
do_stuff: do_stuff.sh subcommand
|
||||
test: "test.sh --option --install example --version \\"0.1.1\\""
|
||||
version: 0.1.1`,
|
||||
reuse: {
|
||||
problem: `name = "example",
|
||||
version = "0.1.1",
|
||||
scripts = {
|
||||
test = m#"test.sh --option --install example --version "0.1.1""#m,`,
|
||||
diff: `name = "example",
|
||||
version = "0.1.1",
|
||||
scripts = {
|
||||
test = m#"test.sh --option --install #{name} --version "#{version}""#m`,
|
||||
result: `# [...]
|
||||
scripts:
|
||||
do_stuff: do_stuff.sh subcommand
|
||||
test: "test.sh --option --install example --version \\"0.1.2\\""
|
||||
version: 0.1.2`,
|
||||
},
|
||||
};
|
||||
|
||||
const IndexPage = () => {
|
||||
useEffect(() => {
|
||||
Prism.languages.nickel = nickelLanguageDefinition;
|
||||
Prism.highlightAll();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<main className="container content-main-container content">
|
||||
<h1 id="getting-started" className={'main-title'}>Getting started</h1>
|
||||
|
||||
<p>Nickel is still young and the installation process is not yet optimal. Sorry about that! We are focused on improving the
|
||||
experience, so stay tuned. </p>
|
||||
|
||||
<h2 id="build-from-source-using-nix">Build from source using Nix</h2>
|
||||
|
||||
<p>Using <a className={"link-primary"} href="https://nixos.org/">Nix</a> is the easiest way
|
||||
to get a Nickel executable
|
||||
running.</p>
|
||||
|
||||
<ol>
|
||||
<li><p>Clone the <a className={"link-primary"} href="https://github.com/tweag/nickel">Nickel
|
||||
repository</a> and set it as the current directory:</p>
|
||||
|
||||
<pre className={'command-line language-bash'} data-user="devops" data-host="nickel"
|
||||
data-output="2-3:"><code>{codeExamples.withNix.clone}</code></pre>
|
||||
</li>
|
||||
<li><p>Invoke <code>nix-build</code>:</p>
|
||||
<pre className={'command-line language-bash'} data-user="devops" data-host="nickel:~/nickel"
|
||||
data-output="2:"><code>{codeExamples.withNix.build}</code></pre>
|
||||
</li>
|
||||
<li><p>If everything went right, a binary is now available in the <code>result</code> directory:</p>
|
||||
<pre className={'command-line language-bash'} data-user="devops" data-host="nickel:~/nickel"
|
||||
data-output="2:"><code>{codeExamples.withNix.run}</code></pre>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<h2 id="build-from-source-without-nix">Build from source without Nix</h2>
|
||||
|
||||
<p>Please refer to the <a
|
||||
className={"link-primary"}
|
||||
href="https://github.com/tweag/nickel/#getting-started">README</a> of the <a
|
||||
className={"link-primary"}
|
||||
href="https://github.com/tweag/nickel">Nickel repository</a> for alternative ways of building Nickel.</p>
|
||||
|
||||
<h2 id="write-your-first-configuration">Write your first configuration</h2>
|
||||
|
||||
<p> Nickel has advanced features to help you handle and organize complex configurations (gradual typing, contracts, a merge system, and so on).
|
||||
But you'll only have to deal with any of this once you need to.
|
||||
Writing a basic configuration is as simple as writing JSON or YAML. Let us write a manifest of a fictional app:</p>
|
||||
|
||||
<div className={'d-none d-md-block'}>
|
||||
<Playground fit={'code'} mode={modes.YAML} value={codeExamples.firstConfig}/>
|
||||
</div>
|
||||
<div className={'d-block d-md-none'}>
|
||||
<pre><code className={'language-nickel'}>{codeExamples.firstConfig}</code></pre>
|
||||
</div>
|
||||
<p/>This program is composed of <i>record</i>. A record is the same thing as an object in JSON. It is a list of
|
||||
key-value pairs delimited
|
||||
by <code>{'{'}</code> and <code>{'}'}</code>. In general, the values of Nickel map directly to
|
||||
corresponding values in JSON (excluding functions). Thus, the basic datatypes of Nickel are the same as in JSON:
|
||||
<ul>
|
||||
<li>Records (objects), delimited by <code>{'{'}</code> and <code>{'}'}</code>.</li>
|
||||
<li>Strings, delimited by <code>"</code>. The sequence <code>m#"</code> and <code>"#m</code> delimits multiline strings.
|
||||
</li>
|
||||
<li>Numbers</li>
|
||||
<li>Lists, delimited by <code>[</code> and <code>]</code> and separated by <code>,</code>.</li>
|
||||
</ul>
|
||||
|
||||
<p/>Multiline strings are an alternative way of defining strings. Line 11 is an example of such a string. Without diving into the details, multiline strings are
|
||||
useful for:
|
||||
<ul>
|
||||
<li>Write strings spanning several lines, as their name suggests. Multiline strings can be indented at the same
|
||||
level as the surrounding code while still producing the expected result (the common indentation prefix is stripped).
|
||||
</li>
|
||||
<li>Write strings with special characters without having to escape them.</li>
|
||||
</ul>
|
||||
|
||||
In our example, using a multiline string saves us from escaping the recurring double quotes <code>"</code>.
|
||||
<h2 id="export">Export</h2>
|
||||
<p>The ultimate goal of a Nickel program is to produce a static configuration. To do so, save the content of our example above in <code>example.ncl</code> and run <code>nickel export</code>:</p>
|
||||
<pre className={'command-line language-bash'} data-user="devops" data-host="nickel:~/nickel"
|
||||
data-output="2-21:"><code>{codeExamples.export}</code></pre>
|
||||
|
||||
<p>Nickel currently supports exporting to and importing from YAML, TOML and JSON.</p>
|
||||
|
||||
<h2 id="reuse">Reuse</h2>
|
||||
|
||||
<p>Nickel is a programming language. This allows you not only to describe, but to
|
||||
generate data. There's repetition in our previous example:</p>
|
||||
<pre><code className={'language-nickel'}>{codeExamples.reuse.problem}</code></pre>
|
||||
|
||||
<p>The version <code>0.1.1</code> appears both in <code>version</code> and <code>scripts.test</code>.
|
||||
The name <code>example</code> appears both in <code>name</code> and <code>scripts.test</code> as well.
|
||||
Pure aesthetics aside, a more serious issue is inconsistency. If you bump the
|
||||
version number in <code>version</code>, you may forget to do so in the <code>scripts.test</code> as well,
|
||||
ending up wih incoherent version numbers in the same configuration. To remedy the problem, let's have a
|
||||
single source of truth by reusing the value of <code>name</code> and <code>version</code> in <code>scripts.test</code>, using
|
||||
the string interpolation syntax <code>#{'{expr}'}</code>:</p>
|
||||
<pre><code className={'language-nickel'}>{codeExamples.reuse.diff}</code></pre>
|
||||
|
||||
<p>Now, if we change version to <code>0.1.2</code> and export the result, the test script
|
||||
invocation is updated as well:</p>
|
||||
|
||||
<pre><code className={'language-yaml'}>{codeExamples.reuse.result}</code></pre>
|
||||
|
||||
<h2 id="going-further">Going further</h2>
|
||||
|
||||
<p>This short introduction should get you started. Nickel is a
|
||||
full-fledged programming language, featuring higher-order functions, gradual
|
||||
typing, contracts, and more! Additional resources are to come on this website. In the meantime, you can find <a
|
||||
className={"link-primary"}
|
||||
href="https://github.com/tweag/nickel/tree/master/examples">examples in the repository</a>. You will also find more details on the language and its design in the <a
|
||||
className={"link-primary"}
|
||||
href="https://github.com/tweag/nickel/#nickel">README</a> and in the <a
|
||||
className={"link-primary"}
|
||||
href="https://github.com/tweag/nickel/blob/master/RATIONALE.md">design rationale</a>.</p>
|
||||
</main>
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
export default IndexPage
|
@ -1,128 +0,0 @@
|
||||
import * as React from "react"
|
||||
import Layout from "../components/layout"
|
||||
import {StaticImage} from "gatsby-plugin-image";
|
||||
import mergeImage from '../images/merge-2.png';
|
||||
import validateImage from '../images/validate-2.png';
|
||||
import reuseImage from '../images/reuse-2.png';
|
||||
import PlaygroundComponent from "../components/playground-clientside";
|
||||
import modes from "../components/playground/modes";
|
||||
import {Command} from "react-bootstrap-icons";
|
||||
import {
|
||||
faChevronDown,
|
||||
} from '@fortawesome/free-solid-svg-icons';
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||
|
||||
const codeExample = `let conf = {
|
||||
name = "NiCl",
|
||||
version = "0.0.1$",
|
||||
description = "My cool app!"
|
||||
} in
|
||||
|
||||
let SemanticVersion = fun label value =>
|
||||
let pattern = "^\\\\d{1,2}\\\\.\\\\d{1,2}(\\\\.\\\\d{1,2})?$" in
|
||||
if strings.isMatch pattern value then
|
||||
value
|
||||
else
|
||||
let msg = "invalid version number" in
|
||||
contracts.blame (contracts.tag msg label)
|
||||
in
|
||||
|
||||
let AppSchema = {
|
||||
name | Str,
|
||||
version | #SemanticVersion,
|
||||
description | Str,
|
||||
} in
|
||||
|
||||
conf | #AppSchema`;
|
||||
|
||||
/**
|
||||
* Scroll offset after which the scrolldown arrow is hidden, in pixels.
|
||||
* @type {number}
|
||||
*/
|
||||
const HIDE_SCROLLDOWN_ARROW_AFTER = 100;
|
||||
|
||||
const IndexPage = () => {
|
||||
const [isArrowVisible, setArrowVisible] = React.useState(true);
|
||||
|
||||
const onScroll = () => {
|
||||
const currentScroll = document.body.scrollTop || document.documentElement.scrollTop;
|
||||
|
||||
if(isArrowVisible && currentScroll > HIDE_SCROLLDOWN_ARROW_AFTER) {
|
||||
setArrowVisible(false);
|
||||
}
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
window.addEventListener('scroll', onScroll);
|
||||
return () =>
|
||||
window.removeEventListener('scroll', onScroll);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<main className="container main-container">
|
||||
<section className="row first-section-block">
|
||||
<div className="col-12 text-center">
|
||||
<h1 className="main-title mb-4"><StaticImage className={"logo"} src="../images/nickel-logo-2.svg" alt="logo"/><span className="nickel">Nickel</span></h1>
|
||||
<div className="main-subtitle mt-4 mb-4 title-font">Better configuration
|
||||
for less
|
||||
</div>
|
||||
|
||||
<div className="mt-4 mb-4 main-text">
|
||||
Write complex configurations. Modular, correct and boilerplate-free.
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<hr className={'horizontal-sep d-none d-md-block'}/>
|
||||
|
||||
<section className={'row section-block d-none d-md-block'}>
|
||||
<div className="col-12 text-center">
|
||||
<h2 className="mb-4">Try it out. Find the <span className={'landingpage-error'}>error</span>!</h2>
|
||||
<div className="mt-4 mb-4 main-text">
|
||||
This configuration contains an error. Fix it and press <kbd>Ctrl</kbd>+<kbd>Enter</kbd> (or <kbd>Cmd <Command/>
|
||||
</kbd>+<kbd>Enter</kbd>) or click <span className={'btn btn-primary disabled'}>Run</span> to try your solution.
|
||||
</div>
|
||||
<div className={'text-left landingpage-playground-wrapper'}>
|
||||
<PlaygroundComponent value={codeExample} fit={'code'} mode={modes.JSON}/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<hr className={'horizontal-sep'}/>
|
||||
|
||||
<section className="row last-section-block">
|
||||
<div className="col-12 col-lg-4 mb-5 mb-lg-0 main-text text-center landingpage-column">
|
||||
<img src={mergeImage} className="abstract-illustration" alt={""}/>
|
||||
<h3 className="mb-4 mt-4">Merge</h3>
|
||||
<div className="text-left mt-4">
|
||||
Write simple, modular blocks. Merge them into a complex configuration.
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 col-lg-4 mb-5 mb-lg-0 main-text text-center landingpage-column">
|
||||
<img src={validateImage} className="abstract-illustration" alt={""}/>
|
||||
<h3 className="mb-4 mt-4">Verify & Validate</h3>
|
||||
<div className="text-left mt-4">
|
||||
<p>Use (opt-in) static typing to verify functions, if you need to. Let
|
||||
type inference do the boring work.</p>
|
||||
|
||||
<p>Use contracts
|
||||
to validate your data and ensure they conform to a given schema.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 col-lg-4 main-text text-center landingpage-column">
|
||||
<img src={reuseImage} className="abstract-illustration" alt={""}/>
|
||||
<h3 className="mb-4 mt-4">Reuse</h3>
|
||||
<div className="text-left lt-4">
|
||||
Don't use hacks, don't reinvent the wheel: Nickel is a
|
||||
programming language. Factorize. Reuse the generic parts. Import external libraries.
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{isArrowVisible && <FontAwesomeIcon icon={faChevronDown} className={'scroll-down-arrow'}/>}
|
||||
</main>
|
||||
</Layout>
|
||||
)
|
||||
};
|
||||
|
||||
export default IndexPage
|
@ -1,21 +0,0 @@
|
||||
import * as React from "react"
|
||||
import Layout from "../components/layout"
|
||||
import PlaygroundComponent from "../components/playground-clientside";
|
||||
|
||||
const PlaygroundPage = () => {
|
||||
return (
|
||||
<Layout>
|
||||
<div className={"container-fluid playground-main-container d-flex flex-column"}>
|
||||
<section className={"row"}>
|
||||
<div className={"col-12 text-center"}>
|
||||
<h1 className="main-title">Playground</h1>
|
||||
Experiment with the Nickel REPL online!
|
||||
</div>
|
||||
</section>
|
||||
<PlaygroundComponent/>
|
||||
</div>
|
||||
</Layout>
|
||||
)
|
||||
};
|
||||
|
||||
export default PlaygroundPage
|
@ -1,28 +0,0 @@
|
||||
/**
|
||||
* Simple language definition for Nickel to use with the Prism highlighting library.
|
||||
* @type {{number: RegExp, string: RegExp[], builtin: RegExp, punctuation: RegExp[], comment: RegExp, keyword: RegExp[], operator: RegExp[]}}
|
||||
*/
|
||||
const nickel = {
|
||||
comment: /\/\/.+/,
|
||||
string: [
|
||||
{ pattern: /m(#+)"(.|\n)*?"\1m/, greedy: true},
|
||||
{ pattern: /".*?"/, greedy: true},
|
||||
],
|
||||
operator: [
|
||||
/>/, />=/, /</, /<=/, /&/, /==/, /&&/, /\|\|/, /!/, /\+/, /@/, /-/, /\+\+/,
|
||||
],
|
||||
keyword: [
|
||||
/let/,
|
||||
/in/,
|
||||
/fun/,
|
||||
/switch/,
|
||||
/forall/,
|
||||
],
|
||||
punctuation: [
|
||||
/:/, /,/, /;/, /\{/, /}/, /\(/, /\)/, /=/, /\|/, /#/,
|
||||
],
|
||||
number: /[0-9]*\.?[0-9]+/,
|
||||
builtin: /((?:Dyn)|(?:Num)|(?:Bool)|(?:Str)|(?:List:(?:[a-zA-Z0-9_]*)?))/,
|
||||
};
|
||||
|
||||
export default nickel;
|
@ -1,108 +0,0 @@
|
||||
@use "sass:map";
|
||||
@use "sass:color";
|
||||
|
||||
$theme-colors: (
|
||||
"primary": #e0c3fc,
|
||||
"secondary": #8ec5fc,
|
||||
);
|
||||
|
||||
@import '~bootstrap/scss/bootstrap.scss';
|
||||
|
||||
a:hover { text-decoration: none};
|
||||
|
||||
/* Correctly style links with our custom primary and secondary colors */
|
||||
|
||||
@mixin link($color) {
|
||||
color: $color;
|
||||
&:hover, &:focus {
|
||||
color: color.adjust($color, $lightness: -30%);
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: color.adjust($color, $lightness: +10%) !important;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin link-theme($color) {
|
||||
$selected: map.get($theme-colors, $color);
|
||||
@include link(color.adjust($selected, $lightness: -25%));
|
||||
}
|
||||
|
||||
.link-primary {
|
||||
@include link-theme("primary");
|
||||
}
|
||||
|
||||
.link-secondary {
|
||||
@include link-theme("secondary");
|
||||
}
|
||||
|
||||
.link-footer {
|
||||
color: #757575;
|
||||
&:hover, &:focus {
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
|
||||
.horizontal-sep {
|
||||
height: 5px;
|
||||
background: map.get($theme-colors, "primary");
|
||||
}
|
||||
|
||||
.content {
|
||||
h1, h2, h3, h4, h5 {
|
||||
margin-bottom: 0.5em;
|
||||
margin-top: 2.5em;
|
||||
}
|
||||
|
||||
h1:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Augment bootstrap with breakpoint variants of w-100, w-50, and so on. */
|
||||
|
||||
@each $breakpoint in map-keys($grid-breakpoints) {
|
||||
@each $size, $length in $sizes {
|
||||
@include media-breakpoint-up($breakpoint) {
|
||||
.w-#{$breakpoint}-#{$size} {width: $length !important;}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Animation of the landing page's scrolldown arrow */
|
||||
|
||||
.scroll-down-arrow {
|
||||
height: 60px;
|
||||
width: 80px;
|
||||
margin: 0px 0 0 -40px;
|
||||
line-height: 60px;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: 0px;
|
||||
color: black;
|
||||
text-align: center;
|
||||
font-size: 70px;
|
||||
z-index: 100;
|
||||
text-decoration: none;
|
||||
text-shadow: 0px 0px 3px rgba(0, 0, 0, 0.4);
|
||||
|
||||
-webkit-animation: ca3_fade_move_down 1.5s ease-in-out infinite;
|
||||
-moz-animation: ca3_fade_move_down 1.5s ease-in-out infinite;
|
||||
animation: ca3_fade_move_down 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@-webkit-keyframes ca3_fade_move_down {
|
||||
0% { -webkit-transform:translate(0,-20px); opacity: 0; }
|
||||
50% { opacity: 1; }
|
||||
100% { -webkit-transform:translate(0,20px); opacity: 0; }
|
||||
}
|
||||
@-moz-keyframes ca3_fade_move_down {
|
||||
0% { -moz-transform:translate(0,-20px); opacity: 0; }
|
||||
50% { opacity: 1; }
|
||||
100% { -moz-transform:translate(0,20px); opacity: 0; }
|
||||
}
|
||||
@keyframes ca3_fade_move_down {
|
||||
0% { transform:translate(0,-20px); opacity: 0; }
|
||||
50% { opacity: 1; }
|
||||
100% { transform:translate(0,20px); opacity: 0; }
|
||||
}
|
@ -1,256 +0,0 @@
|
||||
.main-title {
|
||||
font-size: 3em;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.main-subtitle {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.main-text {
|
||||
font-size: 1.5em;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.playground-loader {
|
||||
font-size: 1.5em;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.secondary-text {
|
||||
font-size: 1.3em;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.main-container {
|
||||
padding-top: 5em;
|
||||
padding-bottom: 5em;
|
||||
}
|
||||
|
||||
.content-main-container {
|
||||
margin-top: 5em;
|
||||
padding-bottom: 5em;
|
||||
}
|
||||
|
||||
.playground-main-container {
|
||||
margin-top: 5em;
|
||||
margin-bottom: 5em;
|
||||
height: 80vh;
|
||||
}
|
||||
|
||||
.playground-terminal {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.playground-tab {
|
||||
margin-top: 2.5em;
|
||||
margin-bottom: 0.5em;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.playground-container {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.playground-terminal-container {
|
||||
max-height: 100%;
|
||||
padding: 10px;
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
#playground-input {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.playground-nav-item {
|
||||
color: black;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.playground-nav-item.active {
|
||||
color: black !important;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.section-block {
|
||||
padding-top: 12.5em;
|
||||
padding-bottom: 12.5em;
|
||||
}
|
||||
|
||||
.first-section-block {
|
||||
padding-top: 7.5em;
|
||||
padding-bottom: 12.5em;
|
||||
}
|
||||
|
||||
.last-section-block {
|
||||
padding-top: 12.5em;
|
||||
}
|
||||
|
||||
.landingpage-column {
|
||||
padding-left: 2em;
|
||||
padding-right: 2em;
|
||||
}
|
||||
|
||||
.landingpage-playground-wrapper {
|
||||
margin-top: 5em;
|
||||
}
|
||||
|
||||
.landingpage-error {
|
||||
color: #D44045;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: auto;
|
||||
width: 100px;
|
||||
vertical-align: middle;
|
||||
margin-right: 0.3em;
|
||||
}
|
||||
|
||||
.logo-navbar {
|
||||
height: auto;
|
||||
width: 50px;
|
||||
vertical-align: middle;
|
||||
margin-right: 0.3em;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Century Gothic";
|
||||
src: url('../fonts/century-gothic.woff')
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Bureau Grotesque";
|
||||
src: url('../fonts/bureau-grotesque-55-regular.woff')
|
||||
}
|
||||
|
||||
.nickel {
|
||||
font-family: "Century Gothic";
|
||||
}
|
||||
|
||||
.title-font, h1, h2, h3 {
|
||||
font-family: "Bureau Grotesque";
|
||||
}
|
||||
|
||||
.list-elt {
|
||||
padding-top: 0.2em;
|
||||
padding-bottom: 0.2em;
|
||||
}
|
||||
|
||||
.abstract-illustration {
|
||||
height: 6em;
|
||||
}
|
||||
|
||||
.code {
|
||||
word-wrap: normal;
|
||||
background-color: #f8f9fa;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.code pre {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.code pre > code {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
word-break: normal;
|
||||
white-space: pre;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.full-page {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.overflow-scroll {
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
.asciinema-iframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.ansi-monokai {
|
||||
background: #2F3129;
|
||||
color: #8F908A;
|
||||
}
|
||||
|
||||
.ansi-monokai code {
|
||||
color: #8F908A;
|
||||
}
|
||||
|
||||
.ansi-black-bg {
|
||||
color: #002b36;
|
||||
}
|
||||
|
||||
.ansi-black-fg .ansi-bright-black-fg {
|
||||
color: #073642;
|
||||
}
|
||||
|
||||
.ansi-green-bg {
|
||||
color: #586e75;
|
||||
}
|
||||
|
||||
.ansi-yellow-bg {
|
||||
color: #657b83;
|
||||
}
|
||||
|
||||
.ansi-blue-bg {
|
||||
color: #839496;
|
||||
}
|
||||
|
||||
.ansi-cyan-bg {
|
||||
color: #93a1a1;
|
||||
}
|
||||
|
||||
.ansi-white-fg .ansi-bright-white-fg {
|
||||
color: #eee8d5;
|
||||
}
|
||||
|
||||
.ansi-white-bg {
|
||||
color: #fdf6e3;
|
||||
}
|
||||
|
||||
.ansi-yellow-fg .ansi-bright-yellow-fg {
|
||||
color: #b58900;
|
||||
}
|
||||
|
||||
.ansi-red-bg {
|
||||
color: #cb4b16;
|
||||
}
|
||||
|
||||
.ansi-red-fg, .ansi-bright-red-fg {
|
||||
color: #dc322f;
|
||||
}
|
||||
|
||||
.ansi-magenta-fg, .ansi-bright-magenta-fg {
|
||||
color: #d33682;
|
||||
}
|
||||
|
||||
.ansi-magenta-fb {
|
||||
color: #6c71c4;
|
||||
}
|
||||
|
||||
.ansi-blue-fg, .ansi-bright-blue-fg {
|
||||
color: #268bd2;
|
||||
}
|
||||
|
||||
.ansi-cyan-fg, .ansi-bright-cyan-fg {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
.ansi-green-fg, .ansi-bright-green-fg {
|
||||
color: #859900;
|
||||
}
|
||||
|
||||
.ansi-bright-black-fg, .ansi-bright-red-fg, .ansi-bright-green-fg, .ansi-bright-yellow-fg,
|
||||
.ansi-bright-blue-fg, .ansi-bright-magenta-fg, .ansi-bright-cyan-fg, .ansi-bright-white-fg {
|
||||
font-weight: bold;
|
||||
}
|