diff --git a/uvls/src/core/ast.rs b/uvls/src/core/ast.rs index 4cab8614..996715b2 100644 --- a/uvls/src/core/ast.rs +++ b/uvls/src/core/ast.rs @@ -1,6 +1,7 @@ use crate::core::*; use check::ErrorInfo; use hashbrown::HashMap; +use itertools::Itertools; use ropey::Rope; use semantic::FileID; use std::hash::Hash; @@ -10,11 +11,11 @@ use tower_lsp::lsp_types::Url; use tree_sitter::Tree; use ustr::Ustr; use util::lsp_range; +pub mod collapse; mod def; +pub mod graph; mod transform; mod visitor; -pub mod graph; -pub mod collapse; pub use def::*; pub use visitor::*; //Easy to work with AST parsing and util. @@ -177,6 +178,12 @@ impl Ast { fn all_features(&self) -> impl Iterator { (0..self.features.len()).map(Symbol::Feature) } + fn get_feature(&self, index: usize) -> Option<&Feature> { + self.features.get(index) + } + fn get_attribute(&self, index: usize) -> Option<&Attribute> { + self.attributes.get(index) + } fn all_attributes(&self) -> impl Iterator { (0..self.attributes.len()).map(Symbol::Attribute) } @@ -243,6 +250,12 @@ impl AstDocument { pub fn all_features(&self) -> impl Iterator { self.ast.all_features() } + pub fn get_feature(&self, index: usize) -> Option<&Feature> { + self.ast.get_feature(index) + } + pub fn get_attribute(&self, index: usize) -> Option<&Attribute> { + self.ast.get_attribute(index) + } pub fn all_attributes(&self) -> impl Iterator { self.ast.all_attributes() } @@ -394,6 +407,26 @@ impl AstDocument { }) }) } + // returns all Symbols with same name, used for cardinality resolving + pub fn get_all_entities(&self, path: &[Ustr]) -> Vec { + let ustr_path = Ustr::from(path.iter().map(|s| s.to_string()).join(".").as_str()); + let mut res = vec![]; + for i in 0..self.ast.features.len() { + if ustr_path == self.get_feature(i).unwrap().name.name { + res.push(Symbol::Feature(i)); + } + } + let mut path: Vec = path.iter().cloned().collect(); + if let Some(name) = path.pop() { + let features = self.get_all_entities(&path); + for i in features { + if let Some(sym) = self.ast.index.get(&(i, name, SymbolKind::Attribute)) { + res.push(*sym); + } + } + } + res + } //prefix of sym from root pub fn prefix(&self, mut sym: Symbol) -> Vec { if matches!(sym, Symbol::Import(..)) { diff --git a/uvls/src/core/ast/def.rs b/uvls/src/core/ast/def.rs index 28f9d278..6e2e307c 100644 --- a/uvls/src/core/ast/def.rs +++ b/uvls/src/core/ast/def.rs @@ -55,6 +55,7 @@ pub enum Type { Bool, Void, Namespace, + Object, } #[derive(Clone, Debug)] @@ -67,10 +68,8 @@ pub enum GroupMode { } #[derive(Clone, Debug)] pub enum Cardinality { - From(usize), Range(usize, usize), - Max(usize), - Any, + Fixed, } #[derive(Clone, Debug)] pub enum LanguageLevelMajor { @@ -112,6 +111,8 @@ pub struct Feature { pub name: SymbolSpan, pub cardinality: Option, pub ty: Type, + pub duplicate: bool, + pub first_cardinality_child: bool, // used to fix same name problem } #[derive(Clone, Debug)] pub struct Import { @@ -136,6 +137,7 @@ pub struct Attribute { pub name: SymbolSpan, pub value: ValueDecl, pub depth: u32, + pub duplicate: bool, } #[derive(Clone, Debug)] pub struct Keyword { diff --git a/uvls/src/core/ast/graph.rs b/uvls/src/core/ast/graph.rs index ab249b97..d9817478 100644 --- a/uvls/src/core/ast/graph.rs +++ b/uvls/src/core/ast/graph.rs @@ -289,6 +289,7 @@ fn opt_int(node: Node, state: &mut VisitorGraph) -> Option { None } } +// returns cardinality for Node fn opt_cardinality(node: Node, state: &mut VisitorGraph) -> Option { let begin = node.child_by_field_name("begin"); let end = node.child_by_field_name("end"); @@ -297,9 +298,8 @@ fn opt_cardinality(node: Node, state: &mut VisitorGraph) -> Option opt_int(begin, state)?, opt_int(end.unwrap(), state)?, )), - (Some(begin), Some("*")) => Some(Cardinality::From(opt_int(begin, state)?)), - (None, Some("int")) => Some(Cardinality::Max(opt_int(end.unwrap(), state)?)), - (_, _) => Some(Cardinality::Any), + (None, Some("int")) => Some(Cardinality::Range(0, opt_int(end.unwrap(), state)?)), + (_, _) => Some(Cardinality::Range(0, 1)), } } @@ -451,7 +451,7 @@ fn opt_equation(node: Node) -> Option { _ => None, } } -fn visit_constraint(graph: &mut VisitorGraph, _: GraphNode) { +fn visit_constraint(graph: &mut VisitorGraph, _: GraphNode,_ :&bool) { graph.add_constraint(graph.node().byte_range()); } fn opt_bool(graph: &mut VisitorGraph) -> bool { @@ -500,34 +500,34 @@ fn opt_value(graph: &mut VisitorGraph) -> Value { } } -fn visit_attribute_value(graph: &mut VisitorGraph, _: GraphNode) { +fn visit_attribute_value(graph: &mut VisitorGraph, _: GraphNode, _:&bool) { graph.goto_field("name"); let _ = opt_name(graph).unwrap(); } -fn visit_constraint_list(graph: &mut VisitorGraph, parent: GraphNode) { +fn visit_constraint_list(graph: &mut VisitorGraph, parent: GraphNode, _:&bool) { loop { if graph.kind() == "constraint" { - visit_children_arg(graph, parent.clone(), visit_constraint); + visit_children_arg(graph, parent.clone(), &false,visit_constraint); } if !graph.goto_next_sibling() { break; } } } -fn visit_attributes(graph: &mut VisitorGraph, parent: &GraphNode) { +fn visit_attributes(graph: &mut VisitorGraph, parent: &GraphNode,_:&bool) { loop { match graph.kind() { "attribute_constraints" => { - visit_children_arg(graph, parent.clone(), visit_constraint_list); + visit_children_arg(graph, parent.clone(),&false, visit_constraint_list); } "attribute_constraint" => { visit_children(graph, |state| { debug_assert!(state.goto_kind("constraint")); - visit_children_arg(state, parent.clone(), visit_constraint); + visit_children_arg(state, parent.clone(), &false, visit_constraint); }); } "attribute_value" => { - visit_children_arg(graph, parent.clone(), visit_attribute_value); + visit_children_arg(graph, parent.clone(),&false, visit_attribute_value); } _ => {} } @@ -557,10 +557,10 @@ fn visit_feature(graph: &mut VisitorGraph, parent: &mut GraphNode, name: SymbolS loop { match graph.cursor().node().kind() { "attributes" => { - visit_children_arg(graph, &feature, visit_attributes); + visit_children_arg(graph, &feature, &false,visit_attributes); } "blk" => { - visit_children_arg(graph, &mut feature, visit_blk_decl); + visit_children_arg(graph, &mut feature, &false,visit_blk_decl); } _ => {} } @@ -586,14 +586,14 @@ fn visit_group(graph: &mut VisitorGraph, mut parent: &mut GraphNode, mode: Group parent.group_mode = Some(mode); loop { if graph.kind() == "blk" { - visit_children_arg(graph, &mut *parent, visit_blk_decl); + visit_children_arg(graph, &mut *parent, &false, visit_blk_decl); } if !graph.goto_next_sibling() { break; } } } -fn visit_blk_decl(graph: &mut VisitorGraph, parent: &mut GraphNode) { +fn visit_blk_decl(graph: &mut VisitorGraph, parent: &mut GraphNode,_:&bool) { graph.goto_field("header"); match graph.kind() { "name" => { @@ -633,7 +633,7 @@ fn visit_blk_decl(graph: &mut VisitorGraph, parent: &mut GraphNode) { visit_group(graph, parent, mode); } "cardinality" => { - let card = opt_cardinality(graph.node(), graph).unwrap_or(Cardinality::Any); + let card = opt_cardinality(graph.node(), graph).unwrap_or(Cardinality::Fixed); visit_group(graph, parent, GroupMode::Cardinality(card)); } _ => { } @@ -643,7 +643,7 @@ fn visit_features(graph: &mut VisitorGraph) { let mut root: GraphNode = GraphNode::root(graph.root_name.clone()); loop { if graph.kind() == "blk" { - visit_children_arg(graph, &mut root, visit_blk_decl); + visit_children_arg(graph, &mut root,&false, visit_blk_decl); } if !graph.goto_next_sibling() { break; @@ -653,8 +653,8 @@ fn visit_features(graph: &mut VisitorGraph) { fn visit_constraint_decl(graph: &mut VisitorGraph) { loop { match graph.kind() { - "constraint" | "ref" => visit_children_arg(graph, GraphNode::root(None), visit_constraint), - "name" => visit_constraint(graph, GraphNode::root(None)), + "constraint" | "ref" => visit_children_arg(graph, GraphNode::root(None), &false,visit_constraint), + "name" => visit_constraint(graph, GraphNode::root(None),&false), _ => {} } if !graph.goto_next_sibling() { diff --git a/uvls/src/core/ast/transform.rs b/uvls/src/core/ast/transform.rs index 5ab49643..cc41c3c1 100644 --- a/uvls/src/core/ast/transform.rs +++ b/uvls/src/core/ast/transform.rs @@ -1,17 +1,17 @@ use crate::core::*; +use ast::visitor::Visitor; use ast::visitor::*; use ast::Ast; use check::ErrorInfo; -use parse::*; -use semantic::FileID; -use util::{node_range}; use log::info; +use parse::*; use ropey::Rope; +use semantic::FileID; use std::borrow::{Borrow, Cow}; use tokio::time::Instant; use tower_lsp::lsp_types::{DiagnosticSeverity, Url}; use tree_sitter::{Node, Tree, TreeCursor}; -use ast::visitor::Visitor; +use util::node_range; //Transform a tree-sitter tree into the Ast ECS via recursive decent //While parsing we keep a mutable state to store entities and errors #[derive(Clone)] @@ -98,46 +98,50 @@ impl<'a> VisitorState<'a> { while let Some((node, scope, depth)) = stack.pop() { let new_scope = if let Some(name) = self.ast.name(node) { match node { - Symbol::Feature(..) => { - if let Some(old) = self + Symbol::Feature(i) => { + // removes duplicate feature error for cardinality + if !self.ast.get_feature(i).unwrap().duplicate { + if let Some(old) = self .ast .index .insert((Symbol::Root, name, SymbolKind::Feature), node) - { - self.errors.push(ErrorInfo { - location: self.ast.lsp_range(node, self.source).unwrap(), - severity: DiagnosticSeverity::ERROR, - weight: 20, - msg: "duplicate feature".to_string(), - }); - self.errors.push(ErrorInfo { - location: self.ast.lsp_range(old, self.source).unwrap(), - severity: DiagnosticSeverity::ERROR, - weight: 20, - msg: "duplicate feature".to_string(), - }) + { + self.errors.push(ErrorInfo { + location: self.ast.lsp_range(node, self.source).unwrap(), + severity: DiagnosticSeverity::ERROR, + weight: 20, + msg: "duplicate feature".to_string(), + }); + self.errors.push(ErrorInfo { + location: self.ast.lsp_range(old, self.source).unwrap(), + severity: DiagnosticSeverity::ERROR, + weight: 20, + msg: "duplicate feature".to_string(), + }) + } } node } Symbol::Attribute(i) => { if let Some(old) = self - .ast - .index - .insert((scope, name, SymbolKind::Attribute), node) + .ast + .index + .insert((scope, name, SymbolKind::Attribute), node) { - self.errors.push(ErrorInfo { - location: self.ast.lsp_range(node, self.source).unwrap(), - severity: DiagnosticSeverity::ERROR, - weight: 20, - msg: "duplicate attribute".to_string(), - }); - self.errors.push(ErrorInfo { - location: self.ast.lsp_range(old, self.source).unwrap(), - severity: DiagnosticSeverity::ERROR, - weight: 20, - msg: "duplicate attribute".to_string(), - }); - }; + self.errors.push(ErrorInfo { + location: self.ast.lsp_range(node, self.source).unwrap(), + severity: DiagnosticSeverity::ERROR, + weight: 20, + msg: "duplicate attribute".to_string(), + }); + self.errors.push(ErrorInfo { + location: self.ast.lsp_range(old, self.source).unwrap(), + severity: DiagnosticSeverity::ERROR, + weight: 20, + msg: "duplicate attribute".to_string(), + }); + + } self.ast.attributes[i].depth = depth; node } @@ -259,7 +263,6 @@ fn check_simple_blk(state: &mut VisitorState, kind: &str) { } } - //report error if a node has cardinality or attributes fn check_no_extra_blk(state: &mut VisitorState, kind: &str) { match state.cursor.field_name() { @@ -491,9 +494,8 @@ fn opt_cardinality(node: Node, state: &mut VisitorState) -> Option opt_int(begin, state)?, opt_int(end.unwrap(), state)?, )), - (Some(begin), Some("*")) => Some(Cardinality::From(opt_int(begin, state)?)), - (None, Some("int")) => Some(Cardinality::Max(opt_int(end.unwrap(), state)?)), - (_, _) => Some(Cardinality::Any), + (None, Some("int")) => Some(Cardinality::Range(opt_int(end.unwrap(), state)?, opt_int(end.unwrap(), state)?)), + (_, _) => Some(Cardinality::Range(0, 1)), } } @@ -557,26 +559,43 @@ fn opt_function_args(state: &mut VisitorState) -> Option> { } fn check_langlvls(state: &mut VisitorState, searched_lang_lvl: LanguageLevel) { - - if state.ast.includes.is_empty() { // no includes means, that implicitly everything is included + if state.ast.includes.is_empty() { + // no includes means, that implicitly everything is included return (); } - let includes: Vec = state.ast.includes.clone().into_iter().map(|x| x.lang_lvl).collect(); + let includes: Vec = state + .ast + .includes + .clone() + .into_iter() + .map(|x| x.lang_lvl) + .collect(); - fn check_sub_lang_lvls<'a, F, G, L: PartialEq>(mut m: F, mut get: G, sub_lang_lvls: Vec, includes: Vec, any: L) -> bool - where F: FnMut(&LanguageLevel) -> bool, G: FnMut(&LanguageLevel) -> Option>, { - sub_lang_lvls.is_empty() && includes.iter().any(|x| m(x)) || - sub_lang_lvls.iter().fold(false, |mut res, val: &L| -> bool { - for i in includes.iter(){ - let x = get(i).unwrap_or(vec![]); - if x.contains(&any) || x.contains(&val) { - res = true; - break; + fn check_sub_lang_lvls<'a, F, G, L: PartialEq>( + mut m: F, + mut get: G, + sub_lang_lvls: Vec, + includes: Vec, + any: L, + ) -> bool + where + F: FnMut(&LanguageLevel) -> bool, + G: FnMut(&LanguageLevel) -> Option>, + { + sub_lang_lvls.is_empty() && includes.iter().any(|x| m(x)) + || sub_lang_lvls + .iter() + .fold(false, |mut res, val: &L| -> bool { + for i in includes.iter() { + let x = get(i).unwrap_or(vec![]); + if x.contains(&any) || x.contains(&val) { + res = true; + break; + } } - } - res - }) + res + }) } if !match searched_lang_lvl.borrow() { @@ -593,7 +612,13 @@ fn check_langlvls(state: &mut VisitorState, searched_lang_lvl: LanguageLevel) { |x| -> Option> {match x { LanguageLevel::Type(l) => Some(l.to_vec()), _ => None}}, s.clone(), includes, LanguageLevelType::Any) } { - state.push_error(10, format!("Operation does not correspond includes. Please include {:?}", searched_lang_lvl)) + state.push_error( + 10, + format!( + "Operation does not correspond includes. Please include {:?}", + searched_lang_lvl + ), + ) } } @@ -632,7 +657,7 @@ fn opt_integer(state: &mut VisitorState) -> Option { visit_children(state, |state| { if state.goto_field("arg") { let n: Box = opt_numeric(state)?.into(); - let out = Some(Expr::Integer{op: op.clone(), n}); + let out = Some(Expr::Integer { op: op.clone(), n }); if state.goto_next_sibling() && state.goto_field("arg") { state.push_error(30, "expected exactly one argument"); } @@ -678,11 +703,11 @@ fn opt_numeric(state: &mut VisitorState) -> Option { }) } "nested_expr" => visit_children(state, opt_numeric).map(|c| c.content), - "function" => {match state.slice(state.child_by_name("op")?).borrow() { + "function" => match state.slice(state.child_by_name("op")?).borrow() { "sum" | "avg" => { check_langlvls(state, LanguageLevel::Arithmetic(vec![LanguageLevelArithmetic::Aggregate])); opt_aggregate(state) - }, + } "len" => { check_langlvls(state, LanguageLevel::Type(vec![LanguageLevelType::StringConstraints])); if state.child_by_name("tail").is_some() { @@ -702,16 +727,16 @@ fn opt_numeric(state: &mut VisitorState) -> Option { None } }) - }, + } "floor" | "ceil" => { check_langlvls(state, LanguageLevel::Type(vec![LanguageLevelType::NumericConstraints])); opt_integer(state) - }, + } _ => { state.push_error(30, "unknown function"); None } - }}, + }, _ => { state.push_error(40, "found a constraint, expected a expression"); None @@ -799,7 +824,7 @@ fn opt_constraint(state: &mut VisitorState) -> Option { } .map(|content| ConstraintDecl { span, content }) } -fn visit_constraint(state: &mut VisitorState, parent: Symbol) { +fn visit_constraint(state: &mut VisitorState, parent: Symbol, _duplicate: &bool) { if let Some(cons) = opt_constraint(state) { state.add_constraint(cons, parent); } @@ -852,7 +877,7 @@ fn opt_value(state: &mut VisitorState) -> Value { } } -fn visit_attribute_value(state: &mut VisitorState, parent: Symbol) { +fn visit_attribute_value(state: &mut VisitorState, parent: Symbol, duplicate: &bool) { state.goto_field("name"); let name = opt_name(state).unwrap(); let sym = Symbol::Attribute(state.ast.attributes.len()); @@ -867,38 +892,39 @@ fn visit_attribute_value(state: &mut VisitorState, parent: Symbol) { span: state.node().byte_range(), }, depth: 0, + duplicate: duplicate.clone(), }); if has_children { - visit_children_arg(state, sym, visit_attributes); + visit_children_arg(state, sym, &duplicate, visit_attributes); } } -fn visit_constraint_list(state: &mut VisitorState, parent: Symbol) { +fn visit_constraint_list(state: &mut VisitorState, parent: Symbol, duplicate: &bool) { loop { if state.kind() == "constraint" { - visit_children_arg(state, parent, visit_constraint); + visit_children_arg(state, parent, duplicate, visit_constraint); } if !state.goto_next_sibling() { break; } } } -fn visit_attributes(state: &mut VisitorState, parent: Symbol) { +fn visit_attributes(state: &mut VisitorState, parent: Symbol, duplicate: &bool) { loop { match state.kind() { "attribute_constraints" => { if state.child_by_name("tail").is_some() { state.push_error(10, "tailing comma unsupported"); } - visit_children_arg(state, parent, visit_constraint_list); + visit_children_arg(state, parent, duplicate, visit_constraint_list); } "attribute_constraint" => { visit_children(state, |state| { debug_assert!(state.goto_kind("constraint")); - visit_children_arg(state, parent, visit_constraint); + visit_children_arg(state, parent, duplicate, visit_constraint); }); } "attribute_value" => { - visit_children_arg(state, parent, visit_attribute_value); + visit_children_arg(state, parent, duplicate, visit_attribute_value); } _ => {} } @@ -908,7 +934,13 @@ fn visit_attributes(state: &mut VisitorState, parent: Symbol) { } } -fn visit_feature(state: &mut VisitorState, parent: Symbol, name: SymbolSpan, ty: Type) { +fn visit_feature( + state: &mut VisitorState, + parent: Symbol, + name: SymbolSpan, + ty: Type, + duplicate: bool, +) { match parent { Symbol::Feature(..) => { state.push_error(40, "features have to be separated by groups"); @@ -926,20 +958,59 @@ fn visit_feature(state: &mut VisitorState, parent: Symbol, name: SymbolSpan, ty: .and_then(|n| { check_langlvls(state, LanguageLevel::Arithmetic(vec![LanguageLevelArithmetic::FeatureCardinality])); opt_cardinality(n, state) - }), + }) + .or_else(|| Some(Cardinality::Fixed)), + duplicate: duplicate.clone(), + first_cardinality_child: true, }; - let sym = Symbol::Feature(state.ast.features.len()); - state.ast.features.push(feature); - state.push_child(parent, sym); + + // remaps feature to different entities of the cardinality + let mut sym = vec![]; + + match feature.clone().cardinality.unwrap() { + Cardinality::Fixed => { + sym.push(Symbol::Feature(state.ast.features.len())); + state.ast.features.push(feature.clone()); + state.push_child(parent, sym.get(0).unwrap().clone()); + } + Cardinality::Range(_, max) => { + for i in 0..max { + let mut dup_feature = feature.clone(); + sym.push(Symbol::Feature(state.ast.features.len())); + if i != 0 { + dup_feature.duplicate = true; + dup_feature.first_cardinality_child = false; + } + state.ast.features.push(dup_feature); + state.push_child(parent, sym.get(i).unwrap().clone()); + } + } + } loop { - match state.kind() { - "attributes" => { - visit_children_arg(state, sym, visit_attributes); + for index in 0..sym.len() { + let mut duplicate_par = false; + if let Symbol::Feature(id) = sym.get(index).unwrap() { + duplicate_par = state.ast.get_feature(*id).unwrap().duplicate; } - "blk" => { - visit_children_arg(state, sym, visit_blk_decl); + match state.kind() { + "attributes" => { + visit_children_arg( + state, + sym.get(index).unwrap().clone(), + &duplicate_par, + visit_attributes, + ); + } + "blk" => { + visit_children_arg( + state, + sym.get(index).unwrap().clone(), + &&duplicate_par, + visit_blk_decl, + ); + } + _ => {} } - _ => {} } if !state.goto_next_sibling() { break; @@ -962,7 +1033,7 @@ fn visit_ref(state: &mut VisitorState, parent: Symbol, path: Path) { } } } -fn visit_group(state: &mut VisitorState, parent: Symbol, mode: GroupMode) { +fn visit_group(state: &mut VisitorState, parent: Symbol, mode: GroupMode, duplicate: &bool) { match parent { Symbol::Group(..) => { state.push_error(40, "groups have to be separated by features"); @@ -981,19 +1052,19 @@ fn visit_group(state: &mut VisitorState, parent: Symbol, mode: GroupMode) { loop { check_no_extra_blk(state, "group"); if state.kind() == "blk" { - visit_children_arg(state, sym, visit_blk_decl); + visit_children_arg(state, sym, duplicate, visit_blk_decl); } if !state.goto_next_sibling() { break; } } } -fn visit_blk_decl(state: &mut VisitorState, parent: Symbol) { +fn visit_blk_decl(state: &mut VisitorState, parent: Symbol, duplicate: &bool) { state.goto_field("header"); match state.kind() { "name" => { let name = opt_name(state).unwrap(); - visit_feature(state, parent, name, Type::Bool); + visit_feature(state, parent, name, Type::Bool, *duplicate); } "typed_feature" => { check_langlvls(state, LanguageLevel::Type(vec![])); @@ -1012,7 +1083,7 @@ fn visit_blk_decl(state: &mut VisitorState, parent: Symbol) { Some((opt_name(state).unwrap(), ty)) }) .unwrap(); - visit_feature(state, parent, name, ty); + visit_feature(state, parent, name, ty, *duplicate); } "ref" => { let path = visit_children(state, |state| { @@ -1034,12 +1105,15 @@ fn visit_blk_decl(state: &mut VisitorState, parent: Symbol) { "alternative" => GroupMode::Alternative, _ => GroupMode::Mandatory, }; - visit_group(state, parent, mode); + visit_group(state, parent, mode, duplicate); } "cardinality" => { - check_langlvls(state, LanguageLevel::Boolean(vec![LanguageLevelBoolean::GroupCardinality])); - let card = opt_cardinality(state.node(), state).unwrap_or(Cardinality::Any); - visit_group(state, parent, GroupMode::Cardinality(card)); + check_langlvls( + state, + LanguageLevel::Boolean(vec![LanguageLevelBoolean::GroupCardinality]), + ); + let card = opt_cardinality(state.node(), state).unwrap_or(Cardinality::Fixed); + visit_group(state, parent, GroupMode::Cardinality(card), duplicate); } _ => { state.push_error(40, "expected a feature or group declaration"); @@ -1047,15 +1121,15 @@ fn visit_blk_decl(state: &mut VisitorState, parent: Symbol) { } } fn visit_features(state: &mut VisitorState) { - let keyword = Keyword { - name: state.name(state.node()), - span: state.node().byte_range() - }; + let keyword = Keyword { + name: state.name(state.node()), + span: state.node().byte_range(), + }; state.ast.keywords.push(keyword); loop { check_no_extra_blk(state, "features"); if state.kind() == "blk" { - visit_children_arg(state, Symbol::Root, visit_blk_decl); + visit_children_arg(state, Symbol::Root, &false, visit_blk_decl); } if !state.goto_next_sibling() { break; @@ -1066,8 +1140,10 @@ fn visit_constraint_decl(state: &mut VisitorState) { loop { check_simple_blk(state, "constraints"); match state.kind() { - "constraint" | "ref" => visit_children_arg(state, Symbol::Root, visit_constraint), - "name" => visit_constraint(state, Symbol::Root), + "constraint" | "ref" => { + visit_children_arg(state, Symbol::Root, &false, visit_constraint) + } + "name" => visit_constraint(state, Symbol::Root, &false), _ => {} } if state.kind() == "ref" { diff --git a/uvls/src/core/ast/visitor.rs b/uvls/src/core/ast/visitor.rs index 7e6675ed..19473f76 100644 --- a/uvls/src/core/ast/visitor.rs +++ b/uvls/src/core/ast/visitor.rs @@ -136,14 +136,14 @@ where } } -pub fn visit_children_arg<'a, A, F, T, V>(state: &mut V, arg: A, mut f: F) -> T +pub fn visit_children_arg<'a, A, F, T, V>(state: &mut V, arg: A,duplicate: &bool, mut f: F) -> T where V: Visitor<'a>, - F: FnMut(&mut V, A) -> T, + F: FnMut(&mut V, A,&bool) -> T, T: Default, { if state.goto_first_child() { - let out = stacker::maybe_grow(32 * 1024, 1024 * 1024, || f(state, arg)); + let out = stacker::maybe_grow(32 * 1024, 1024 * 1024, || f(state, arg,duplicate)); state.goto_parent(); out } else { diff --git a/uvls/src/core/check.rs b/uvls/src/core/check.rs index 0dfdf62d..8c0792dc 100644 --- a/uvls/src/core/check.rs +++ b/uvls/src/core/check.rs @@ -10,7 +10,7 @@ use tower_lsp::Client; use tree_sitter::{Node, QueryCursor, Tree}; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct ErrorInfo { pub location: Range, pub severity: DiagnosticSeverity, @@ -35,11 +35,14 @@ impl ErrorInfo { } } pub async fn publish(client: &Client, uri: &Url, err: &[ErrorInfo]) { - if let Some(max) = err.iter().max_by_key(|e| e.weight) { + // reduces cardinality error to one error + let mut reduced_err = vec![]; + err.iter().for_each(|ele| if !reduced_err.contains(ele) {reduced_err.push(ele.clone())}); + if let Some(max) = reduced_err.clone().into_iter().max_by_key(|e| e.weight) { client .publish_diagnostics( uri.clone(), - err.iter() + reduced_err[..].iter() .rev() .filter(|e| e.weight == max.weight) .map(|i| i.clone().diagnostic()) diff --git a/uvls/src/core/config.rs b/uvls/src/core/config.rs index 5b0ebc32..5d48cc18 100644 --- a/uvls/src/core/config.rs +++ b/uvls/src/core/config.rs @@ -1,13 +1,11 @@ -use std::fmt::Display; use crate::core::*; - +use std::fmt::Display; +use crate::ide::completion::*; use ast::*; use check::ErrorInfo; -use crate::ide::completion::*; use parse::SymbolSlice; use semantic::{FileID, RootGraph}; use util::*; - use itertools::Itertools; use log::info; use ropey::Rope; @@ -31,21 +29,57 @@ use ustr::Ustr; // //} //This representation is very compact since it avoids rewriting long import prefixes but slightly -//more complex than just using the direct raw path to external symbols. +//more complex than just using the direct raw path to external symbols. //JSON parsing is done with tree-sitter and not serde because there currently is no solid serde json //crate for span information and partial parsing so error reporting becomes impossible. +// This enum is used for storing a cardinality inside of ConfigEntry +// EntitiyLvl is only used to serialize cardinality. +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub enum CardinalityEntry { + CardinalityLvl(Vec>), + EntitiyLvl(Vec), +} + #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(untagged)] pub enum ConfigValue { Bool(bool), Number(f64), String(String), + Cardinality(CardinalityEntry), +} + +impl Serialize for CardinalityEntry { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeSeq; + match self { + CardinalityEntry::CardinalityLvl(childs) => { + let mut s = serializer.serialize_seq(Some(childs.len()))?; + for child in childs { + let _ = s.serialize_element(&CardinalityEntry::EntitiyLvl(child.to_owned())); + } + s.end() + } + CardinalityEntry::EntitiyLvl(childs) => ConfigEntry::Import( + Path { + names: vec![], + spans: vec![], + }, + childs.to_owned(), + ) + .serialize(serializer), + } + } } impl ConfigValue { pub fn ty(&self) -> Type { match self { + Self::Cardinality(..) => Type::Object, Self::Bool(..) => Type::Bool, Self::Number(..) => Type::Real, Self::String(..) => Type::String, @@ -69,6 +103,7 @@ impl Display for ConfigValue { Self::Bool(x) => write!(f, "{x}"), Self::Number(x) => write!(f, "{x}"), Self::String(x) => write!(f, "{x}"), + Self::Cardinality(_) => Ok(()), } } } @@ -86,7 +121,6 @@ impl Serialize for ConfigEntry { { use serde::ser::SerializeMap; - match self { ConfigEntry::Value(..) => panic!(), ConfigEntry::Import(_, v) => { @@ -107,6 +141,22 @@ impl Serialize for ConfigEntry { } } +// This is necesarry for rust compiler +impl<'de> Deserialize<'de> for ConfigEntry { + fn deserialize(_deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + Ok(ConfigEntry::Value( + Path { + names: vec![], + spans: vec![], + }, + ConfigValue::Bool(true), + )) + } +} + impl ConfigEntry { pub fn is_empty(&self) -> bool { match self { @@ -205,8 +255,36 @@ fn opt_configs(state: &mut State) -> Vec { }); acc.push(ConfigEntry::Import(key, children)); } + "array" => { + let mut children: Vec> = vec![]; + + state.goto_first_child(); + state.goto_field("value"); + state.goto_first_child(); + + visit_siblings(state, |state: &mut State<'_>| { + if state.kind() == "object" { + let children_object = + stacker::maybe_grow(32 * 1024, 1024 * 1024, || { + visit_children(state, |state| opt_configs(state)) + }); + children.push(children_object); + } + }); + state.goto_parent(); + state.goto_parent(); + + acc.push(ConfigEntry::Value( + key, + ConfigValue::Cardinality(CardinalityEntry::CardinalityLvl(children)), + )); + } _ => { - state.push_error_node(val, 30, "expected a number or bool"); + state.push_error_node( + val, + 30, + format!("Expect Number or Bool {:?}", val.kind()), + ); } } } @@ -327,7 +405,11 @@ fn json_path<'a>(mut node: Node, rope: &'a Rope) -> Vec { while let Some(p) = node.parent() { if node.kind() == "object" && p.kind() == "pair" { if let Some(key) = p.child_by_field_name("key").and_then(|k| k.named_child(0)) { - ctx.push(rope.slice(key.byte_range()).to_string().replace(r#"\"""#,"") ) + ctx.push( + rope.slice(key.byte_range()) + .to_string() + .replace(r#"\"""#, ""), + ) } } node = p; @@ -371,12 +453,9 @@ fn find_json_key<'a>(mut root: Node<'a>, source: &Rope, key: &[Ustr]) -> Option< if cursor.goto_first_child() { loop { if let Some(name) = cursor.node().child_by_field_name("key") { - - if source.slice_raw(name.named_child(0)?.byte_range()) == k.as_str() { root = cursor.node().child_by_field_name("value")?; - break; } } @@ -426,41 +505,36 @@ fn estimate_json_item(pos: &Position, source: &Rope) -> Option { let slice: std::borrow::Cow<'_, _> = source.line(line).into(); let start_byte = source.line_to_byte(line); let pos_byte = byte_offset(pos, source) - start_byte; - + RE.captures(&slice) .iter() - .find_map(|cap| { - - - match (cap.get(1), cap.get(2)) { - (Some(key), Some(val)) => { - if key.range().overlaps(pos_byte) { - Some(JSONItem::Key { - key: offset(key.range(), start_byte), - value: Some(offset(val.range(), start_byte)), - }) - } else if val.range().overlaps(pos_byte) { - Some(JSONItem::Value { - key: offset(key.range(), start_byte), - value: offset(val.range(), start_byte), - }) - } else { - None - } + .find_map(|cap| match (cap.get(1), cap.get(2)) { + (Some(key), Some(val)) => { + if key.range().overlaps(pos_byte) { + Some(JSONItem::Key { + key: offset(key.range(), start_byte), + value: Some(offset(val.range(), start_byte)), + }) + } else if val.range().overlaps(pos_byte) { + Some(JSONItem::Value { + key: offset(key.range(), start_byte), + value: offset(val.range(), start_byte), + }) + } else { + None } - (Some(key), None) if key.range().overlaps(pos_byte) => Some(JSONItem::Key { - key: offset(key.range(), start_byte), - value: None, - }), - _ => None, } + (Some(key), None) if key.range().overlaps(pos_byte) => Some(JSONItem::Key { + key: offset(key.range(), start_byte), + value: None, + }), + _ => None, }) .or_else(|| { lazy_static! { static ref RE: Regex = Regex::new(r#""((?:[^"\\]|\\.)*)""#).unwrap(); }; - RE.captures(&slice).iter().find_map(|cap| { info!("Cap: {:#?} ", cap); cap.get(1).and_then(|key| { @@ -480,7 +554,6 @@ fn estimate_json_item(pos: &Position, source: &Rope) -> Option { .chars() .all(|c| c.is_alphanumeric() || c.is_whitespace() || c == '.') { - let start = slice .char_indices() .take_while(|(_, c)| c.is_whitespace()) @@ -495,7 +568,6 @@ fn estimate_json_item(pos: &Position, source: &Rope) -> Option { .unwrap_or_default() .0 + start; - if (start..last + 1).contains(&pos_byte) { Some(JSONItem::FreeKey(offset(start..last + 1, start_byte))) @@ -570,7 +642,7 @@ pub fn completion_query(source: &Rope, tree: &Tree, src_pos: &Position) -> Optio .collect(); info!("path:{path:#?}"); info!("prefix:{prefix:#?}"); - info!("char:{}",source.char(char)); + info!("char:{}", source.char(char)); if source.char(char) == '.' { Some(CompletionQuery { offset: CompletionOffset::Dot, @@ -594,7 +666,7 @@ pub fn completion_query(source: &Rope, tree: &Tree, src_pos: &Position) -> Optio source, )?, }, - prefix:prefix[0..prefix.len().saturating_sub(1)].to_vec(), + prefix: prefix[0..prefix.len().saturating_sub(1)].to_vec(), postfix: path.names.last().map(|s| s.as_str()).unwrap_or("").into(), }) } @@ -618,12 +690,15 @@ pub fn completion_query(source: &Rope, tree: &Tree, src_pos: &Position) -> Optio start: src_pos.clone(), end: src_pos.clone(), }, - key_start:if path.len()>0{ - let start = lsp_position( path.spans[0].start,source).clone().unwrap(); - Some(Range{start:start.clone(),end:start}) - }else{ + key_start: if path.len() > 0 { + let start = lsp_position(path.spans[0].start, source).clone().unwrap(); + Some(Range { + start: start.clone(), + end: start, + }) + } else { None - } + }, }, prefix, postfix: CompactString::new_inline(""), @@ -637,14 +712,17 @@ pub fn completion_query(source: &Rope, tree: &Tree, src_pos: &Position) -> Optio path.spans.last().cloned().unwrap_or(key), source, )?, - key_start:if path.len()>1{ - let start = lsp_position( path.spans[0].start,source).clone().unwrap(); - Some(Range{start:start.clone(),end:start}) - }else{ + key_start: if path.len() > 1 { + let start = lsp_position(path.spans[0].start, source).clone().unwrap(); + Some(Range { + start: start.clone(), + end: start, + }) + } else { None - } + }, }, - prefix:prefix[0..prefix.len().saturating_sub(1)].to_vec(), + prefix: prefix[0..prefix.len().saturating_sub(1)].to_vec(), postfix: path.names.last().map(|s| s.as_str()).unwrap_or("").into(), }) } diff --git a/uvls/src/core/module.rs b/uvls/src/core/module.rs index 2a80f844..c66d3b43 100644 --- a/uvls/src/core/module.rs +++ b/uvls/src/core/module.rs @@ -1,10 +1,10 @@ -use tokio::time::Instant; use crate::core::*; -use resolve; use config::*; use hashbrown::HashMap; use indexmap::IndexSet; use log::info; +use resolve; +use tokio::time::Instant; use ustr::Ustr; use std::sync::Arc; @@ -167,7 +167,8 @@ impl Module { _ => panic!("{src_sym:?} not a value"), } } - //Bind a recursive configuration doc to a linear set of symbols + // Bind a recursive configuration doc to a linear set of symbols + // Add layer for cardinality pub fn resolve_config( &self, doc: &Vec, @@ -179,35 +180,98 @@ impl Module { assert!(self.ok); let mut out = HashMap::new(); let mut out_span = HashMap::new(); - let mut stack = vec![(InstanceID(0), doc.as_slice())]; - while let Some((instance, config)) = stack.pop() { + // offset is used for mapping to different entities of a cardinality and its children + let mut stack = vec![(InstanceID(0), doc.as_slice(), 0 as usize)]; + while let Some((instance, config, offset)) = stack.pop() { let file = self.file(instance); for c in config.iter() { match c { - ConfigEntry::Value(path, val) => { - if let Some(sym) = file - .lookup(Symbol::Root, &path.names, |sym| { - matches!(sym, Symbol::Feature(..) | Symbol::Attribute(..)) - }) - .next() - { - if file.type_of(sym).unwrap() == val.ty() { - out.insert(ModuleSymbol { instance, sym }, val.clone()); - out_span.insert(ModuleSymbol { instance, sym }, path.range()); + ConfigEntry::Value(path, val) => match val { + ConfigValue::Cardinality(cardinality) => match cardinality { + CardinalityEntry::CardinalityLvl(cardinality_lvl) => { + let entries = file.get_all_entities(&path.names.clone()); + let entries = entries.iter().nth(offset); + match entries { + Some(Symbol::Feature(id)) => { + let feature = file.get_feature(id.clone()).unwrap(); + match feature.cardinality { + Some(Cardinality::Range(_, max)) => { + for i in 0..cardinality_lvl.len() { + stack.push(( + instance, + cardinality_lvl.iter().nth(i).unwrap(), + offset * max + i, + )); + } + } + _ => (), + } + } + _ => panic!("unexpected feature"), + } + } + CardinalityEntry::EntitiyLvl(_) => { + panic!("unexpected cardinality level") + } + }, + _ => { + if let Some(sym_ref) = + file.get_all_entities(&path.names).iter().nth(offset) + { + let sym = sym_ref.clone(); + if file.type_of(sym).unwrap() == val.ty() { + out.insert(ModuleSymbol { instance, sym }, val.clone()); + out_span.insert(ModuleSymbol { instance, sym }, path.range()); + } else { + match sym { + Symbol::Feature(i) => { + let feature = file.get_feature(i).unwrap().clone(); + if let Cardinality::Range(_, _) = + feature.cardinality.unwrap() + { + out.insert( + ModuleSymbol { instance, sym }, + val.clone(), + ); + out_span.insert( + ModuleSymbol { instance, sym }, + path.range(), + ); + } else { + err( + path.range(), + format!( + "expected {} got {}", + file.type_of(sym).unwrap(), + val.ty() + ), + ); + } + } + Symbol::Attribute(_) => { + out.insert(ModuleSymbol { instance, sym }, val.clone()); + out_span.insert( + ModuleSymbol { instance, sym }, + path.range(), + ); + } + _ => { + err( + path.range(), + format!( + "expected {} got {}", + file.type_of(sym).unwrap(), + val.ty() + ), + ); + } + } + } } else { - err( - path.range(), - format!( - "expected {} got {}", - file.type_of(sym).unwrap(), - val.ty() - ), - ); + err(path.range(), format!("unresolved value",)); } - } else { - err(path.range(), format!("unresolved value",)); } - } + }, ConfigEntry::Import(path, val) => { if let Some(sym) = file .lookup(Symbol::Root, &path.names, |sym| { @@ -215,7 +279,7 @@ impl Module { }) .find(|sym| matches!(sym, Symbol::Import(..))) { - stack.push((self.get_instance(instance, sym), &val)); + stack.push((self.get_instance(instance, sym), &val, offset)); } else { err(path.range(), format!("unresolved import",)); } @@ -270,21 +334,8 @@ impl ConfigModule { entries.push(entry); } } - file.visit_named_children(Symbol::Root, false, |sym, prefix| match sym { - Symbol::Feature(_) | Symbol::Attribute(_) => { - if let Some(config) = self.values.get(&i.sym(sym)) { - entries.push(ConfigEntry::Value( - Path { - names: prefix.to_vec(), - spans: Vec::new(), - }, - config.clone(), - )) - } - true - } - _ => false, - }); + + entries.append(&mut self.serialize_rec_file(Symbol::Root, file, i)); ConfigEntry::Import( Path { names: path.to_vec(), @@ -293,6 +344,98 @@ impl ConfigModule { entries, ) } + + // serialize file recursive while accommodate for cardinality + fn serialize_rec_file( + &self, + sym: Symbol, + file: &AstDocument, + i: InstanceID, + ) -> Vec { + let mut entries: Vec = Vec::new(); + // used for different entities of cardinality + let mut child_map: HashMap>> = hashbrown::HashMap::new(); + + for child in file.direct_children(sym) { + match child { + Symbol::Feature(id) => { + let feature = file.get_feature(id).unwrap(); + match feature.cardinality { + Some(Cardinality::Fixed) => { + if let Some(config) = self.values.get(&i.sym(child)) { + entries.push(ConfigEntry::Value( + Path { + names: vec![file.name(child).unwrap()], + spans: Vec::new(), + }, + config.clone(), + )) + } + entries.append(&mut self.serialize_rec_file(child, file, i)); + } + Some(Cardinality::Range(_, _)) => { + let mut cardinal_entry: Vec = vec![]; + // add self to cardinality definition + if let Some(config) = self.values.get(&i.sym(child)) { + cardinal_entry.push(ConfigEntry::Value( + Path { + names: vec![file.name(child).unwrap()], + spans: Vec::new(), + }, + config.clone(), + )); + } + cardinal_entry.append(&mut self.serialize_rec_file(child, file, i)); + if cardinal_entry.len() > 0 { + child_map + .get_mut(&file.name(child).unwrap()) + .and_then(|x| { + x.push(cardinal_entry.clone()); + Some(()) + }) + .or_else(|| { + child_map.insert( + file.name(child).unwrap(), + vec![cardinal_entry], + ); + Some(()) + }); + } + } + None => (), + } + } + Symbol::Attribute(_) => { + if let Some(config) = self.values.get(&i.sym(child)) { + entries.push(ConfigEntry::Value( + Path { + names: vec![file.name(sym).unwrap(), file.name(child).unwrap()], + spans: Vec::new(), + }, + config.clone(), + )) + } + entries.append(&mut self.serialize_rec_file(child, file, i)); + } + _ => { + entries.append(&mut self.serialize_rec_file(child, file, i)); + } + } + } + for (cardinality_name, cardinality_childs) in child_map.iter() { + entries.push(ConfigEntry::Value( + Path { + names: vec![cardinality_name.clone()], + spans: Vec::new(), + }, + ConfigValue::Cardinality(CardinalityEntry::CardinalityLvl( + cardinality_childs.clone(), + )), + )) + } + return entries; + } + //Turns a the set of linear configuration values of this module into theire recusive from //used in json pub fn serialize(&self) -> Vec { diff --git a/uvls/src/core/parse.rs b/uvls/src/core/parse.rs index 79638c6f..432105cb 100644 --- a/uvls/src/core/parse.rs +++ b/uvls/src/core/parse.rs @@ -143,7 +143,6 @@ pub fn parse_lang_lvl(node: Node, source: &S) -> Option(node: Node, source: &S) -> Option { - info!("{:?}", node); if let Some(name) = parse_lang_lvl(node, source) { Some(Path { names: vec![name.name], diff --git a/uvls/src/core/semantic.rs b/uvls/src/core/semantic.rs index 8fa61e09..0d51ed1d 100644 --- a/uvls/src/core/semantic.rs +++ b/uvls/src/core/semantic.rs @@ -193,7 +193,6 @@ impl RootGraph { let mut file_paths = HashSet::new(); for file in files.values() { if !file_paths.insert(file.path.as_slice()) { - //info!("{:?}", file.namespace()); if let Some(ns) = file.namespace() { if err.errors.contains_key(&file.id) { err.span(ns.range(), file.id, 100, "namespace already defined"); @@ -202,7 +201,6 @@ impl RootGraph { } } } - //info!("dirty {:?}",dirty); Self { cancel: CancellationToken::new(), cache: Cache::new(old, files, configs, &dirty, revision, err), diff --git a/uvls/src/smt.rs b/uvls/src/smt.rs index d52e79f8..b5be00f8 100644 --- a/uvls/src/smt.rs +++ b/uvls/src/smt.rs @@ -222,7 +222,6 @@ async fn find_fixed( } //let time = Instant::now(); if solve.check_sat().await? { - //info!("check sat {:?}", time.elapsed()); let unknown = state .iter() .filter(|(_, v)| !matches!(*v, SMTValueState::Any)) @@ -230,7 +229,6 @@ async fn find_fixed( format!("{acc} v{}", module.var(*k)) }); let values = solve.values(unknown).await?; - //info!("model {:?}", time.elapsed()); for (s, v) in module.parse_values(&values, base_module) { if let Some(old) = state.get(&s) { match (v, old) { @@ -596,13 +594,11 @@ pub async fn web_view_handler( }) }) .await; - //info!("model: {model:?}"); tx_ui .send(webview::UIAction::UpdateSMTModel(model, tag)) .await?; } - Err(e) => { - //info!("err {e}"); + Err(e) => { inlay_state.maybe_reset(inlay_source).await; tx_ui .send(webview::UIAction::UpdateSMTInvalid(format!("{e}"), tag)) diff --git a/uvls/src/smt/parse.rs b/uvls/src/smt/parse.rs index 52e8856a..ade59e4f 100644 --- a/uvls/src/smt/parse.rs +++ b/uvls/src/smt/parse.rs @@ -190,7 +190,7 @@ mod tests { assert_eq!(n, "test"); let (i,(_,ConfigValue::Bool(n))) = parser.parse(i).unwrap() else {panic!()}; assert_eq!(n, true); - let (i,(_,ConfigValue::Number(n))) = parser.parse(i).unwrap() else {panic!()}; + let (_i,(_,ConfigValue::Number(n))) = parser.parse(i).unwrap() else {panic!()}; assert_approx_eq!(n,1.0); } } diff --git a/uvls/src/smt/smt_lib.rs b/uvls/src/smt/smt_lib.rs index 1062615b..c1f16dbc 100644 --- a/uvls/src/smt/smt_lib.rs +++ b/uvls/src/smt/smt_lib.rs @@ -425,6 +425,7 @@ impl Into for ConfigValue { Self::Bool(b) => Expr::Bool(b), Self::Number(n) => Expr::Real(n), Self::String(s) => Expr::String(s), + Self::Cardinality(_) => Expr::Bool(true), } } } @@ -441,6 +442,34 @@ pub fn uvl2smt(module: &Module, config: &HashMap) -> for f in file.all_features() { builder.push_var(m.sym(f)); } + + for sym_feature in file.all_features() { + if let Symbol::Feature(id) = sym_feature { + let feature = file.get_feature(id).unwrap().clone(); + if let Cardinality::Range(min,_) = feature.cardinality.unwrap_or_else(|| Cardinality::Fixed) { + // Make AtLeast assertion for cardinality feature + if feature.first_cardinality_child { + let mut list = vec![]; + let parent = file.parent(sym_feature, false).unwrap(); + + for child in file.direct_children(parent) { + if let Symbol::Feature(sibling_id) = child { + let sibling = file.get_feature(sibling_id); + if sibling.unwrap().name.name.as_str() == feature.name.name.as_str() + { + list.push(builder.var(m.sym(Symbol::Feature(sibling_id)))); + } + } + } + + builder.assert.push(Assert( + Some(AssertInfo(m.sym(sym_feature), AssertName::GroupMin)), + Expr::AtLeast(min, list), + )); + } + } + } + } } //set config features for (&ms, val) in config @@ -527,14 +556,7 @@ pub fn uvl2smt(module: &Module, config: &HashMap) -> )) } } - GroupMode::Optional | GroupMode::Cardinality(Cardinality::Any) => {} - GroupMode::Cardinality(Cardinality::Max(max)) => { - builder.max_assert(max, &p_bind, m.sym(g)); - } - - GroupMode::Cardinality(Cardinality::From(min)) => { - builder.min_assert(min, &p_bind, m.sym(g)); - } + GroupMode::Optional | GroupMode::Cardinality(Cardinality::Fixed) => {} GroupMode::Cardinality(Cardinality::Range(min, max)) => { builder.min_assert(min, &p_bind, m.sym(g)); builder.max_assert(max, &p_bind, m.sym(g)); @@ -721,14 +743,12 @@ fn translate_expr(decl: &ast::ExprDecl, m: InstanceID, builder: &mut SMTBuilder) ) } } - ast::Expr::Integer { op, n} => { - ( - match op { - ast::IntegerOP::Ceil => Expr::Ceil(translate_expr(n, m, builder).0.into()), - ast::IntegerOP::Floor => Expr::Floor(translate_expr(n, m, builder).0.into()) - }, - Type::Real, - ) - } + ast::Expr::Integer { op, n } => ( + match op { + ast::IntegerOP::Ceil => Expr::Ceil(translate_expr(n, m, builder).0.into()), + ast::IntegerOP::Floor => Expr::Floor(translate_expr(n, m, builder).0.into()), + }, + Type::Real, + ), } } \ No newline at end of file diff --git a/uvls/src/webview.rs b/uvls/src/webview.rs index b1615042..d465662b 100644 --- a/uvls/src/webview.rs +++ b/uvls/src/webview.rs @@ -239,7 +239,7 @@ fn create_file_tree( Symbol::Feature(_) | Symbol::Attribute(_) => { let depth = depth + base_depth - 1; match file.type_of(sym).unwrap() { - Type::String | Type::Real | Type::Bool | Type::Attributes => {} + Type::String | Type::Real | Type::Bool | Type::Attributes | Type::Object => {} _ => { return true; } @@ -732,7 +732,7 @@ pub async fn ui_main( spawn(ui_sync(pipeline.clone(), tx_sync, root)); //Run the event loop ui_event_loop( - id, tx_config, ui_rx, rx_sync, &ui_config, &ui_state, &pipeline, tgt_path + id, tx_config, ui_rx, rx_sync, &ui_config, &ui_state, &pipeline, tgt_path, ) .await?; diff --git a/uvls/src/webview/frontend.rs b/uvls/src/webview/frontend.rs index db0aa038..fe05e9b0 100644 --- a/uvls/src/webview/frontend.rs +++ b/uvls/src/webview/frontend.rs @@ -195,6 +195,9 @@ fn ConfigInput<'a>(cx: Scope<'a, ConfigInputProps<'a>>) -> Element { } } }, + _ => rsx!{ div { + config.to_string() + }}, }; cx.render(rsx! { div{