Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nix/nickel #1067

Draft
wants to merge 73 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
8e38c4e
first draft of nix parser to nickel AST
francois-caddet Jun 13, 2022
5bb0474
print output to pretty nickel
francois-caddet Jun 13, 2022
64bc9e8
parse lists to arrays
francois-caddet Jun 13, 2022
d6ede77
add some BinOp translation.
francois-caddet Jun 14, 2022
028fc24
add string translate
francois-caddet Jun 15, 2022
04b32ec
use more typed `rnix` API
francois-caddet Jun 15, 2022
d2a5a7c
add string interpolation translation
francois-caddet Jun 15, 2022
986114d
add simple function definition and application translation (no patterns)
francois-caddet Jun 15, 2022
bfd6e13
add `!=` operator
francois-caddet Jun 15, 2022
b49f9c5
add `if then else` translation
francois-caddet Jun 15, 2022
7ea4313
translate both nix unary ops
francois-caddet Jun 15, 2022
948d205
Fix let bindings. Now they are mutualy recursives.
francois-caddet Jun 20, 2022
f40f987
first draft of records translation.
francois-caddet Jun 23, 2022
6beca6c
translate function with patterns. does not manage defaults values for
francois-caddet Jun 24, 2022
7a9ccdb
pass node by value to `translate` to avoid useless clones.
francois-caddet Jun 27, 2022
7408cc3
add implication operator translation
francois-caddet Jun 27, 2022
e54603a
Use better typed `rnix` API.
francois-caddet Jun 28, 2022
7466c0a
Parse `Dynamic`s in `translate` function.
francois-caddet Jun 29, 2022
a32df79
add record access translation
francois-caddet Jun 29, 2022
3b4ff2f
add suport of default value for patterns.
francois-caddet Jun 29, 2022
f7beb97
translate `true`, `false` and `null` into the proper value/type
francois-caddet Jun 30, 2022
f3788b0
Update src/nix.rs
francois-caddet Jun 30, 2022
2a86aca
Add a trait `ToNickel`
francois-caddet Nov 16, 2022
a5d9473
code cleaning
francois-caddet Jul 4, 2022
e2c1b6a
panic when redefining builtin value
francois-caddet Jul 4, 2022
6fac91c
Comments updates and panic when using unsuported legacy let.
francois-caddet Jul 5, 2022
39fd5f7
Translate `Path` value to its string repr
francois-caddet Jul 5, 2022
6717c76
clean code for `IfElse` match arm
francois-caddet Jul 5, 2022
a950b7a
Apply suggestions from code review
francois-caddet Jul 5, 2022
bec4ff4
update `ToNickel` trait to take a set of defined vars (`env`)
francois-caddet Jul 5, 2022
465111a
pass `State` around convertion.
francois-caddet Jul 19, 2022
9461cc9
Apply suggestions from code review
francois-caddet Jul 21, 2022
e88f90f
fix "convertion" -> "conversion" typo
francois-caddet Jul 21, 2022
e064169
Nix with parsed and transpiled with let shadowing:
francois-caddet Sep 9, 2022
ad5f72f
Update src/nix.rs
francois-caddet Sep 16, 2022
10c6402
Update src/nix.rs
francois-caddet Sep 29, 2022
40260eb
add rust modul helper for nix compatibility functions in
francois-caddet Sep 29, 2022
e3c2f03
Update stdlib/compat.ncl
francois-caddet Oct 5, 2022
860bda6
last optimizations as proposed by @yannham
francois-caddet Oct 5, 2022
87c71e3
add suport for `?` operator of Nix
francois-caddet Nov 4, 2022
4f9fcbd
add simple test suite for nix compatibility
francois-caddet Oct 28, 2022
e0c88d0
add some tests and discovered issues
francois-caddet Nov 3, 2022
6a6dd03
move Nix's tests to there own tests
francois-caddet Nov 9, 2022
245c90b
fix string concat for Nix
francois-caddet Nov 3, 2022
0d197d8
Update stdlib/compat.ncl
francois-caddet Nov 9, 2022
0ab0760
Update stdlib/compat.ncl
francois-caddet Nov 9, 2022
3736f10
update comment on compat.ncl `add` function as proposed by @yannham
francois-caddet Nov 9, 2022
c1e99dc
ireenable simple string concat testing
francois-caddet Nov 10, 2022
921007d
fix a few warnings and remove a poluting line which came from the last
francois-caddet Dec 20, 2022
b441062
fix checkPhase on nix tests.
francois-caddet Jan 23, 2023
adec631
fix a test to pass also with official Nix interpreter.
francois-caddet Jan 23, 2023
acb6d13
Update `rnix` to 0.11.
francois-caddet Jan 22, 2023
d3cf2bf
Update src/nix.rs
francois-caddet Jan 23, 2023
5823f86
Update src/nix.rs
francois-caddet Jan 23, 2023
3dd0695
clippy pass
francois-caddet Jan 23, 2023
3a555ae
Add a trait `ToNickel`
francois-caddet Jul 2, 2022
a338e9d
pass `State` around convertion.
francois-caddet Jul 19, 2022
af9e0ea
add has_field operator compatibility
francois-caddet Nov 8, 2022
8c05372
add tests on records syntax
francois-caddet Jan 20, 2023
f46365b
fix evaluation of fieldpaths.
francois-caddet Jan 24, 2023
dda02d8
Nix evaluator is now an optional compilation feature
francois-caddet Jan 23, 2023
164f24f
clippy fixes
francois-caddet Jan 24, 2023
fe664c6
first test for `with` keyword
francois-caddet Jan 23, 2023
ab8a8d1
more tests for Nix `with`
francois-caddet Jan 24, 2023
26346bc
tests for Nix `with` inside a function body.
francois-caddet Jan 24, 2023
eb88acf
Small code improvments, comments and documentation for nix/nickel
francois-caddet Jan 27, 2023
58c24df
add some tests for let blocks of Nix
francois-caddet Jan 27, 2023
eb4a959
add the `or <default>` form for a field access in Nix
francois-caddet Jan 27, 2023
cf22ab7
Implementation of the update operator for nix/nickel
francois-caddet Jan 27, 2023
6902241
function renaming in `stdlib/compat` Nickel lib
francois-caddet Jan 30, 2023
ddeff4c
Fix clippy warnings, formatting
yannham Mar 8, 2023
cad83f0
Fix compilation and clippy warnings; tests still fail
yannham Mar 9, 2023
8a8ef4a
Update compat.ncl, fix tests after rebase
yannham Mar 9, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 49 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ markdown = ["termimad"]
repl = ["rustyline", "rustyline-derive", "ansi_term"]
repl-wasm = ["wasm-bindgen", "js-sys", "serde_repr"]
doc = [ "comrak" ]
nix = [ "rnix", "rowan" ]

[build-dependencies]
lalrpop = "0.19.6"
Expand Down Expand Up @@ -64,6 +65,10 @@ comrak = { version = "0.12.1", optional = true, features = [] }
once_cell = "1.14.0"
typed-arena = "2.0.1"

# needed dependencies to be able to interpret nix expr with nickel
rnix = { version = "0.11", optional = true }
rowan = { version = "0.15.10", optional = true }

[dev-dependencies]
pretty_assertions = "1.2.1"
assert_matches = "1.5.0"
Expand Down
2 changes: 2 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@
mkFilter = regexp: path: _type: builtins.match regexp path != null;
lalrpopFilter = mkFilter ".*lalrpop$";
nclFilter = mkFilter ".*ncl$";
nixFilter = mkFilter ".*/tests/nix/.+\\.nix$";
txtFilter = mkFilter ".*txt$";
snapFilter = mkFilter ".*snap$";
in
Expand All @@ -174,6 +175,7 @@
builtins.any (filter: filter path type) [
lalrpopFilter
nclFilter
nixFilter
txtFilter
snapFilter
filterCargoSources
Expand Down
4 changes: 2 additions & 2 deletions lsp/nls/src/requests/completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ impl IdentWithType {
if name.is_ascii() {
String::from(name)
} else {
format!("\"{}\"", name)
format!("\"{name}\"")
}
}
let doc = || {
Expand Down Expand Up @@ -814,7 +814,7 @@ mod tests {
let actual = get_identifier_path(input);
let expected: Option<Vec<_>> =
expected.map(|path| path.iter().map(|s| String::from(*s)).collect());
assert_eq!(actual, expected, "test failed: {}", case_name)
assert_eq!(actual, expected, "test failed: {case_name}")
}
}

Expand Down
36 changes: 35 additions & 1 deletion src/bin/nickel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ use nickel_lang::repl::query_print;
use nickel_lang::repl::rustyline_frontend;
use nickel_lang::term::{RichTerm, Term};
use nickel_lang::{serialize, serialize::ExportFormat};
#[cfg(feature = "nix")]
use std::io::Read;
use std::path::{Path, PathBuf};
use std::{
fs::{self, File},
Expand Down Expand Up @@ -40,6 +42,10 @@ struct Opt {
/// Available subcommands.
#[derive(StructOpt, Debug)]
enum Command {
/// translate Nix input to Nickel code.
/// Only a POC, main target is to be able to run Nix code on nickel.
/// May never be a complet source to source transformation.
Nixin,
/// Converts the parsed representation (AST) back to Nickel source code and prints it. Used for
/// debugging purpose
PprintAst {
Expand Down Expand Up @@ -108,6 +114,34 @@ fn main() {

#[cfg(not(feature = "repl"))]
eprintln!("error: this executable was not compiled with REPL support");
} else if let Some(Command::Nixin) = opts.command {
#[cfg(feature = "nix")]
{
use nickel_lang::cache::Cache;
use nickel_lang::cache::ErrorTolerance;
use nickel_lang::pretty::*;
use pretty::BoxAllocator;

let mut buf = String::new();
let mut cache = Cache::new(ErrorTolerance::Strict);
let mut out: Vec<u8> = Vec::new();
opts.file
.map(std::fs::File::open)
.map(|f| f.and_then(|mut f| f.read_to_string(&mut buf)))
.unwrap_or_else(|| std::io::stdin().read_to_string(&mut buf))
.unwrap_or_else(|err| {
eprintln!("Error when reading input: {err}");
process::exit(1)
});
let allocator = BoxAllocator;
let file_id = cache.add_source("<stdin>.nix", buf.as_bytes()).unwrap();
let rt = nickel_lang::nix::parse(&cache, file_id).unwrap();
let doc: DocBuilder<_, ()> = rt.pretty(&allocator);
doc.render(80, &mut out).unwrap();
println!("{}", String::from_utf8_lossy(&out).as_ref());
}
#[cfg(not(feature = "nix"))]
eprintln!("error: this executable was not compiled with Nix evaluation support");
} else {
let mut program = opts
.file
Expand Down Expand Up @@ -158,7 +192,7 @@ fn main() {
})
}
Some(Command::Typecheck) => program.typecheck(),
Some(Command::Repl { .. }) => unreachable!(),
Some(Command::Repl { .. }) | Some(Command::Nixin) => unreachable!(),
#[cfg(feature = "doc")]
Some(Command::Doc { ref output }) => output
.as_ref()
Expand Down
30 changes: 30 additions & 0 deletions src/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ use void::Void;
#[derive(Clone, Copy, Eq, Debug, PartialEq)]
pub enum InputFormat {
Nickel,
Nix,
Json,
Yaml,
Toml,
Expand All @@ -37,6 +38,19 @@ impl InputFormat {
fn from_path_buf(path_buf: &Path) -> Option<InputFormat> {
match path_buf.extension().and_then(OsStr::to_str) {
Some("ncl") => Some(InputFormat::Nickel),
Some("nix") => {
#[cfg(feature = "nix")]
{
Some(InputFormat::Nix)
}
#[cfg(not(feature = "nix"))]
{
eprintln!(
"error: this executable was not compiled with Nix evaluation support"
);
None
}
}
Some("json") => Some(InputFormat::Json),
Some("yaml") | Some("yml") => Some(InputFormat::Yaml),
Some("toml") => Some(InputFormat::Toml),
Expand Down Expand Up @@ -457,6 +471,22 @@ impl Cache {

Ok((t, parse_errs))
}
// TODO: Error management for parse errors.
// May be better to throw an error instead of panicing if nickel has been compiled
// without Nix support
InputFormat::Nix => {
#[cfg(feature = "nix")]
{
Ok((
crate::nix::parse(self, file_id).unwrap(),
ParseErrors::default(),
))
}
#[cfg(not(feature = "nix"))]
{
panic!("error: this executable was not compiled with Nix evaluation support")
}
}
InputFormat::Json => serde_json::from_str(self.files.source(file_id))
.map(|t| (t, ParseErrors::default()))
.map_err(|err| ParseError::from_serde_json(err, file_id, &self.files)),
Expand Down
38 changes: 38 additions & 0 deletions src/conversion.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//! This module contains a trait to implement on every thing you want to be convertible into nickel
//! AST.
//! It should be used mostly for AST to AST convertion (e.g.: nix to nickel). Effectively the
//! `translate` function require to pass a `codespan::FileId` so it's not wors to use it for
//! convertions which not imply a file/stream input. For these convertions, prefer `Into`/`From`
//! standart traits.

use crate::term::RichTerm;
use codespan::FileId;
use std::collections::HashSet;

/// State of the conversion. It contains the definitions in scope of the currently converted node (e.g.:
/// `with` environments, declared variables, current file id...), required for elaborate compilation (`with`).
#[derive(Clone)]
pub struct State {
/// The current transformation file ID.
pub file_id: FileId,
/// Variables in scope.
pub env: HashSet<String>,
/// With scope. List of the records on which has been applied a with in the current scope.
pub with: Vec<RichTerm>,
}

pub trait ToNickel: Sized {
/// Used when converting a full file. Actually call `translate` with an initial `State`.
fn to_nickel(self, file_id: FileId) -> RichTerm {
let state = State {
file_id,
env: HashSet::new(),
with: Vec::new(),
};
self.translate(&state)
}

/// Use to convert an expression or a term of the source language to nickel.
/// For a complet exemple, you can see `crate::nix`, the convertion from Nix to Nickel.
fn translate(self, state: &State) -> RichTerm;
}
21 changes: 21 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,11 @@ impl IntoDiagnostics<FileId> for ParseErrors {
/// An error occurring during parsing.
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum ParseError {
#[cfg(feature = "nix")]
/// Temporary Nix error variant.
/// TODO: because parsing errors are almost the same between Nix and Nickel, Have a seamless
/// convertion from Nix errors to Nickel ones could be a good improvement.
NixParseError(FileId),
/// Unexpected end of file.
UnexpectedEOF(FileId, /* tokens expected by the parser */ Vec<String>),
/// Unexpected token.
Expand Down Expand Up @@ -1399,6 +1404,22 @@ impl IntoDiagnostics<FileId> for ParseError {
_stdlib_ids: Option<&Vec<FileId>>,
) -> Vec<Diagnostic<FileId>> {
let diagnostic = match self {
// TODO: improve error management for nix parser.
#[cfg(feature = "nix")]
ParseError::NixParseError(file_id) => {
let end = files.source_span(file_id).end();
let start = files.source_span(file_id).start();
Diagnostic::error()
.with_message(format!(
"error parsing nix file {}",
files.name(file_id).to_string_lossy()
))
.with_labels(vec![primary(&RawSpan {
start,
end,
src_id: file_id,
})])
}
ParseError::UnexpectedEOF(file_id, _expected) => {
let end = files.source_span(file_id).end();
Diagnostic::error()
Expand Down
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
pub mod cache;
pub mod conversion;
pub mod deserialize;
pub mod destructuring;
pub mod environment;
pub mod error;
pub mod eval;
pub mod identifier;
pub mod label;
#[cfg(feature = "nix")]
pub mod nix;
pub mod parser;
pub mod position;
pub mod pretty;
Expand Down
Loading