Skip to content

Commit

Permalink
feat: impl format tool.
Browse files Browse the repository at this point in the history
  • Loading branch information
Peefy committed Jun 15, 2022
1 parent 457f7a5 commit 39e21ce
Show file tree
Hide file tree
Showing 47 changed files with 750 additions and 6 deletions.
9 changes: 9 additions & 0 deletions kclvm/Cargo.lock

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

29 changes: 29 additions & 0 deletions kclvm/tools/Cargo.lock

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

3 changes: 3 additions & 0 deletions kclvm/tools/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ edition = "2021"
[dependencies]
indexmap = "1.0"
fancy-regex = "0.7.1"
walkdir = "2"
anyhow = "1.0"

kclvm-ast = {path = "../ast", version = "0.1.0"}
kclvm-error = {path = "../error", version = "0.1.0"}
kclvm-parser = {path = "../parser", version = "0.1.0"}
kclvm-config = {path = "../config", version = "0.1.0"}

[dev-dependencies]
pretty_assertions = "1.2.1"
90 changes: 90 additions & 0 deletions kclvm/tools/src/format/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
//! [kclvm_tools::format] module mainly contains some functions of language formatting,
//! the main API function is `format`, which accepts a path to be formatted and
//! formatted options.
//!
//! The basic principle is to call the [kclvm_parser::parse_file] function to parse the
//! AST Module, and then use the AST printer [kclvm_tools::printer::print_ast_module]
//! to print it as source code string.
use anyhow::{anyhow, Result};
use std::path::Path;

use crate::{printer::print_ast_module, util::get_kcl_files};
use kclvm_parser::parse_file;

#[cfg(test)]
mod tests;

/// FormatOptions contains two options:
/// - is_stdout: whether to output the formatted result to stdout.
/// - recursively: whether to recursively traverse a folder and format all KCL files in it.
#[derive(Debug, Default)]
pub struct FormatOptions {
pub is_stdout: bool,
pub recursively: bool,
}

/// Formats kcl file or directory path contains kcl files and
/// returns the changed file paths.
///
/// # Examples
///
/// ```no_run
/// use kclvm_tools::format::{format, FormatOptions};
///
/// // Format a single file.
/// format("path_to_a_single_file.k", &FormatOptions::default()).unwrap();
/// // Format a folder contains kcl files
/// format("path_to_a_folder", &FormatOptions::default()).unwrap();
/// ```
pub fn format<P: AsRef<Path>>(path: P, opts: &FormatOptions) -> Result<Vec<String>> {
let mut changed_paths: Vec<String> = vec![];
let path_ref = path.as_ref();
if path_ref.is_dir() {
for file in &get_kcl_files(path, opts.recursively)? {
if format_file(file, opts)? {
changed_paths.push(file.clone())
}
}
} else if path_ref.is_file() {
let file = path_ref.to_str().unwrap().to_string();
if format_file(&file, opts)? {
changed_paths.push(file)
}
}
if !opts.is_stdout {
let n = changed_paths.len();
println!(
"KCL format done and {} {} formatted:",
n,
if n <= 1 { "file was" } else { "files were" }
);
for p in &changed_paths {
println!("{}", p);
}
}
Ok(changed_paths)
}

/// Format a file and returns whether the file has been formatted and modified.
fn format_file(file: &str, opts: &FormatOptions) -> Result<bool> {
let src = std::fs::read_to_string(file)?;
let (source, is_formatted) = format_source(&src)?;
if opts.is_stdout {
println!("{}", source);
} else {
std::fs::write(file, &source)?
}
Ok(is_formatted)
}

/// Format a code source and return the formatted source and
/// whether the source is changed.
fn format_source(src: &str) -> Result<(String, bool)> {
let module = match parse_file("", Some(src.to_string())) {
Ok(module) => module,
Err(err) => return Err(anyhow!("{}", err)),
};
let formatted_src = print_ast_module(&module);
let is_formatted = src != formatted_src;
Ok((formatted_src, is_formatted))
}
4 changes: 4 additions & 0 deletions kclvm/tools/src/format/test_data/format_data/assert.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
assert True if True, "message"
# Comment
assert False if data, "message"
assert 1
3 changes: 3 additions & 0 deletions kclvm/tools/src/format/test_data/format_data/assert.input
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
assert True if True, "message"
assert False if data , "message" # Comment
assert 1
4 changes: 4 additions & 0 deletions kclvm/tools/src/format/test_data/format_data/blankline.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
a = 1
b = 2
c = 3
d = 4
12 changes: 12 additions & 0 deletions kclvm/tools/src/format/test_data/format_data/blankline.input
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

a=1


b= 2


c =3



d = 4
9 changes: 9 additions & 0 deletions kclvm/tools/src/format/test_data/format_data/breakline.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import math
schema Base:
name: str

schema Person(Base):
age: int

person = Person {}

6 changes: 6 additions & 0 deletions kclvm/tools/src/format/test_data/format_data/breakline.input
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import math
schema Base:
name: str
schema Person(Base):
age: int
person = Person{}
13 changes: 13 additions & 0 deletions kclvm/tools/src/format/test_data/format_data/check.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
schema Person:
firstName: str = "John"
lastName: str
times: int

check:
len(lastName) > 0 if times > 5

JohnDoe = Person {
"lastName": "Doe"
"times": 10
}

12 changes: 12 additions & 0 deletions kclvm/tools/src/format/test_data/format_data/check.input
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

schema Person:
firstName: str = "John"
lastName: str
times: int
check:
len(lastName) > 0 if times > 5

JohnDoe = Person {
"lastName": "Doe"
"times":10
}
80 changes: 80 additions & 0 deletions kclvm/tools/src/format/test_data/format_data/codelayout.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import math as alias_math
schema Person(Base):
# inline comment
name: str
age: int

check:
age > 0 if age, "age must > 0"

person = Person {
name: "Alice"
age: 18
}

if True:
a = 1
elif True:
b = 2
else:
c = 3
d = 1 + 2
e = (1 + 2)
f = [1, 2, 3]
g = {"key": "value"}
# block comment
print(1)
dct = {"key": "value"}
lst = [1, 2, 3]
h = dct['key']
i = lst[1]
x = 1
y = 2
long_variable = 3
i = i + 1
submitted += 1
x = x * 2 - 1
hypot2 = x * x + y * y
_c = (a + b) * (a - b)
_b = 2
_c = 3
_d = 4
_value = (1 + 2 * 3)
_value = (1 + 2 * 3)
_value = 1 + -2 * ~3
_list = [1, 2, 3]
_list = [*_list, [4, 5, 6]]
_list = [*_list, [4, 5, 6]]
_dict = {**{"k": "v"}, **{"k": "v"}}
a = [1, 2, 3]
b = [
1
2
3
4
5
6
]
_dict = {
"k1": "v1"
"k2": "v2"
"k3": "v3"
"k4": "v4"
"k5": "v5"
}
foo = 1
if foo is not None:
_a = 1
_dict |= {}
hello = "world{}".format(1)[2:4:].lower()
range_int = [i for i in range(10) if i > 1]

op = 1 + 2 - -3 + (3 - 1) // 3
op += 1
op -= 12 + 23
print(" ", end='')
log = math.log(12)
aa = 1
assert aa == 1 if aa, "message"
aaaa = (1 + 2 / 2) if _a == 2 + +134.3 else ("a" * 3)
bbbb = "{}".format(a)
Loading

0 comments on commit 39e21ce

Please sign in to comment.