Skip to content

Commit

Permalink
feat: top level scalar value plan. (#556)
Browse files Browse the repository at this point in the history
feat: scalar value plan.
  • Loading branch information
Peefy committed May 22, 2023
1 parent c83b5d0 commit 1654ef3
Show file tree
Hide file tree
Showing 26 changed files with 247 additions and 55 deletions.
5 changes: 2 additions & 3 deletions kclvm/cmd/src/test_data/cases/import_konfig_1/main.k
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@ main = container.Main {
name = "sss"
}
aa = typeof(main, full_name=True)
appConfiguration: frontend.Server {
cc = typeof(frontend.Server {
image = "nginx"
mainContainer = {
name = "nginx"
}
}
cc = typeof(appConfiguration.mainContainer, full_name=True)
}.mainContainer, full_name=True)
77 changes: 58 additions & 19 deletions kclvm/compiler/src/codegen/llvm/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ use crate::value;

use super::OBJECT_FILE_SUFFIX;

/// SCALAR_KEY denotes the temp scalar key for the global variable json plan process.
const SCALAR_KEY: &str = "";
/// Float type string width mapping
pub const FLOAT_TYPE_WIDTH_MAPPING: Map<&str, usize> = phf_map! {
"half" => 16,
Expand All @@ -54,7 +56,12 @@ pub const FLOAT_TYPE_WIDTH_MAPPING: Map<&str, usize> = phf_map! {
pub type CompileResult<'a> = Result<BasicValueEnum<'a>, kcl_error::KCLError>;

/// The compiler scope.
#[derive(Debug, Default)]
pub struct Scope<'ctx> {
/// Scalars denotes the expression statement values without attribute.
pub scalars: RefCell<Vec<BasicValueEnum<'ctx>>>,
/// schema_scalar_idxdenotes whether a schema exists in the scalar list.
pub schema_scalar_idx: RefCell<usize>,
pub variables: RefCell<IndexMap<String, PointerValue<'ctx>>>,
pub closures: RefCell<IndexMap<String, PointerValue<'ctx>>>,
pub arguments: RefCell<IndexSet<String>>,
Expand Down Expand Up @@ -959,11 +966,7 @@ impl<'ctx> ProgramCodeGen for LLVMCodeGenContext<'ctx> {
if pkg_scopes.contains_key(pkgpath) {
return;
}
let scopes = vec![Rc::new(Scope {
variables: RefCell::new(IndexMap::default()),
closures: RefCell::new(IndexMap::default()),
arguments: RefCell::new(IndexSet::default()),
})];
let scopes = vec![Rc::new(Scope::default())];
pkg_scopes.insert(String::from(pkgpath), scopes);
}
let msg = format!("pkgpath {} is not found", pkgpath);
Expand Down Expand Up @@ -1029,11 +1032,7 @@ impl<'ctx> ProgramCodeGen for LLVMCodeGenContext<'ctx> {
let mut pkg_scopes = self.pkg_scopes.borrow_mut();
let msg = format!("pkgpath {} is not found", current_pkgpath);
let scopes = pkg_scopes.get_mut(&current_pkgpath).expect(&msg);
let scope = Rc::new(Scope {
variables: RefCell::new(IndexMap::default()),
closures: RefCell::new(IndexMap::default()),
arguments: RefCell::new(IndexSet::default()),
});
let scope = Rc::new(Scope::default());
scopes.push(scope);
}

Expand Down Expand Up @@ -1393,6 +1392,31 @@ impl<'ctx> LLVMCodeGenContext<'ctx> {
global_var.as_pointer_value()
}

/// Append a scalar value into the scope.
pub fn add_scalar(&self, scalar: BasicValueEnum<'ctx>, is_schema: bool) {
let current_pkgpath = self.current_pkgpath();
let mut pkg_scopes = self.pkg_scopes.borrow_mut();
let scopes = pkg_scopes
.get_mut(&current_pkgpath)
.expect(&format!("pkgpath {} is not found", current_pkgpath));
if let Some(last) = scopes.last_mut() {
let mut scalars = last.scalars.borrow_mut();
// TODO: To avoid conflicts, only the last schema scalar expressions are allowed.
let mut schema_scalar_idx = last.schema_scalar_idx.borrow_mut();
if is_schema {
// Remove the last schema scalar.
if *schema_scalar_idx < scalars.len() {
scalars.remove(*schema_scalar_idx);
}
// Override the last schema scalar.
scalars.push(scalar);
*schema_scalar_idx = scalars.len() - 1;
} else {
scalars.push(scalar);
}
}
}

/// Append a variable into the scope
pub fn add_variable(&self, name: &str, pointer: PointerValue<'ctx>) {
let current_pkgpath = self.current_pkgpath();
Expand Down Expand Up @@ -1890,23 +1914,38 @@ impl<'ctx> LLVMCodeGenContext<'ctx> {
pub fn globals_to_json_str(&self) -> BasicValueEnum<'ctx> {
let current_pkgpath = self.current_pkgpath();
let mut pkg_scopes = self.pkg_scopes.borrow_mut();
let msg = format!("pkgpath {} is not found", current_pkgpath);
let scopes = pkg_scopes.get_mut(&current_pkgpath).expect(&msg);
let globals = scopes
.last_mut()
.expect(kcl_error::INTERNAL_ERROR_MSG)
.variables
.borrow_mut();
let scopes = pkg_scopes
.get_mut(&current_pkgpath)
.expect(&format!("pkgpath {} is not found", current_pkgpath));
// The global scope.
let scope = scopes.last().expect(kcl_error::INTERNAL_ERROR_MSG);
let scalars = scope.scalars.borrow();
let globals = scope.variables.borrow();
// Construct a plan object.
let global_dict = self.dict_value();
// Deal scalars
for scalar in scalars.iter() {
self.dict_safe_insert(global_dict, SCALAR_KEY, scalar.clone(), 0, -1);
}
// Deal global variables
for (name, ptr) in globals.iter() {
// Omit private variables and function variables
if name.starts_with(kclvm_runtime::KCL_PRIVATE_VAR_PREFIX) {
continue;
}
let value = self.builder.build_load(*ptr, "");
self.dict_safe_insert(global_dict, name.as_str(), value, 0, -1);
let value_dict = self.dict_value();
self.dict_safe_insert(value_dict, name.as_str(), value, 0, -1);
self.dict_safe_insert(global_dict, SCALAR_KEY, value_dict, 0, -1);
}
self.build_call(&ApiFunc::kclvm_value_plan_to_json.name(), &[global_dict])
// Plan result to json string.
self.build_call(
&ApiFunc::kclvm_value_plan_to_json.name(),
&[self.dict_get(
global_dict,
self.native_global_string(SCALAR_KEY, "").into(),
)],
)
}

/// Insert a dict entry including key, value, op and insert_index into the dict.
Expand Down
8 changes: 5 additions & 3 deletions kclvm/compiler/src/codegen/llvm/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,12 @@ impl<'ctx> TypedResultWalker<'ctx> for LLVMCodeGenContext<'ctx> {
check_backtrack_stop!(self);
let mut result = self.ok_result();
for expr in &expr_stmt.exprs {
// Ignore the doc string
if !matches!(&expr.node, ast::Expr::StringLit(..)) {
result = self.walk_expr(expr);
let scalar = self.walk_expr(expr)?;
// Only non-call expressions are allowed to emit values bacause of the function void return type.
if !matches!(expr.node, ast::Expr::Call(_)) {
self.add_scalar(scalar, matches!(expr.node, ast::Expr::Schema(_)));
}
result = Ok(scalar);
}
result
}
Expand Down
14 changes: 0 additions & 14 deletions kclvm/runner/src/exec_data/expr.k

This file was deleted.

32 changes: 19 additions & 13 deletions kclvm/runtime/src/value/val_union.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,30 +290,36 @@ impl ValueRef {
);
if union_context.conflict {
union_context.path_backtrace.reverse();
let confilct_key = union_context.path_backtrace.last().unwrap();
let conflict_key = union_context.path_backtrace.last().unwrap();
let path_string = union_context.path_backtrace.join(".");

// build override_example
// build note
// it will be like:
// {...} | {
// ...
// b = {...}
// ...
// }

let override_example = format!(
let note = format!(
" {{...}} | {{\n ...\n {} = {}\n ...\n }}",
confilct_key, union_context.delta_json
conflict_key, union_context.delta_json
);

panic!(
"conflicting values on the attribute '{}' between :\n {}\nand\n {}\nwith union path :\n {}\ntry operator '=' to override the attribute, like:\n{}",
confilct_key,
union_context.obj_json,
union_context.delta_json,
path_string,
override_example,
)
if conflict_key.is_empty() {
panic!(
"conflicting values between {} and {}",
union_context.delta_json, union_context.obj_json
);
} else {
panic!(
"conflicting values on the attribute '{}' between :\n {}\nand\n {}\nwith union path :\n {}\ntry operator '=' to override the attribute, like:\n{}",
conflict_key,
union_context.obj_json,
union_context.delta_json,
path_string,
note,
);
}
}
ret
}
Expand Down
2 changes: 1 addition & 1 deletion kclvm/version/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright 2021 The KCL Authors. All rights reserved.

pub const VERSION: &str = "0.4.6.3";
pub const CHECK_SUM: &str = "2b1bf940fc528448d982d39ec917372e";
pub const CHECK_SUM: &str = "eb2f4d1aabc2a287c298bc35c5fcfec1";

/// Get kCL full version string with the format `{version}-{check_sum}`.
#[inline]
Expand Down
2 changes: 2 additions & 0 deletions test/grammar/builtins/yaml/output_2/main.k
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,5 @@ print("---")
print(yaml.encode(_person, ignore_none=True), end="")
print("---")
print(yaml.encode(_person, ignore_private=True, ignore_none=True), end="")
print("---")
person = _person
11 changes: 11 additions & 0 deletions test/grammar/builtins/yaml/output_2/stdout.golden
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,14 @@ data:
- 2
labels:
key1: value1
---
person:
name: Alice
age: 18
data:
- 1
- 2
- null
labels:
key1: value1
key3: null
14 changes: 14 additions & 0 deletions test/grammar/scalar/config/multi_config_inst_0/main.k
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
http.server = {
listen = 80
}
}
{
http.server = {
listen = 8080
location = {
root = "/var/www/html"
index = "index.html"
}
}
}
6 changes: 6 additions & 0 deletions test/grammar/scalar/config/multi_config_inst_0/stdout.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
http:
server:
listen: 8080
location:
root: /var/www/html
index: index.html
9 changes: 9 additions & 0 deletions test/grammar/scalar/config/single_config_inst_0/main.k
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
http.server = {
listen = 80
location = {
root = "/var/www/html"
index = "index.html"
}
}
}
6 changes: 6 additions & 0 deletions test/grammar/scalar/config/single_config_inst_0/stdout.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
http:
server:
listen: 80
location:
root: /var/www/html
index: index.html
2 changes: 2 additions & 0 deletions test/grammar/scalar/invalid/conflict_0/main.k
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
1
2
19 changes: 19 additions & 0 deletions test/grammar/scalar/invalid/conflict_0/stderr.golden.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import os
import sys

import kclvm.kcl.error as kcl_error

cwd = os.path.dirname(os.path.realpath(__file__))
kcl_error.print_kcl_error_message(
kcl_error.get_exception(
err_type=kcl_error.ErrType.EvaluationError_TYPE,
file_msgs=[
kcl_error.ErrFileMsg(
filename=cwd + "/main.k",
line_no=2
)
],
arg_msg="conflicting values between 2 and 1"
)
, file=sys.stdout
)
2 changes: 2 additions & 0 deletions test/grammar/scalar/invalid/conflict_1/main.k
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
a = 1
1
19 changes: 19 additions & 0 deletions test/grammar/scalar/invalid/conflict_1/stderr.golden.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import os
import sys

import kclvm.kcl.error as kcl_error

cwd = os.path.dirname(os.path.realpath(__file__))
kcl_error.print_kcl_error_message(
kcl_error.get_exception(
err_type=kcl_error.ErrType.EvaluationError_TYPE,
file_msgs=[
kcl_error.ErrFileMsg(
filename=cwd + "/main.k",
line_no=2
)
],
arg_msg="conflicting values between {...} and 1"
)
, file=sys.stdout
)
1 change: 1 addition & 0 deletions test/grammar/scalar/number/number_0/main.k
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1
1 change: 1 addition & 0 deletions test/grammar/scalar/number/number_0/stdout.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1
2 changes: 2 additions & 0 deletions test/grammar/scalar/number/number_1/main.k
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
_a = 1
_a
1 change: 1 addition & 0 deletions test/grammar/scalar/number/number_1/stdout.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1
29 changes: 29 additions & 0 deletions test/grammar/scalar/schema/multi_schema_inst_0/main.k
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
schema Nginx:
"""Schema for Nginx configuration files"""
http: Http

schema Http:
server: Server

schema Server:
listen: int | str # The attribute `listen` can be int type or a string type.
location?: Location # Optional, but must be non-empty when specified

schema Location:
root: str
index: str

Nginx {
http.server = {
listen = 80
}
}
Nginx {
http.server = {
listen = 8080
location = {
root = "/var/www/html"
index = "index.html"
}
}
}
6 changes: 6 additions & 0 deletions test/grammar/scalar/schema/multi_schema_inst_0/stdout.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
http:
server:
listen: 8080
location:
root: /var/www/html
index: index.html
Loading

0 comments on commit 1654ef3

Please sign in to comment.