Compare commits
18 Commits
v1.0.7
...
paultag/re
Author | SHA1 | Date | |
---|---|---|---|
199d891f82 | |||
f04eafc7bf | |||
c162f2909f | |||
3eb4625b8f | |||
f1ba113893 | |||
872e9a9c4e | |||
171e5536e7 | |||
f984dad87f | |||
84d9e76926 | |||
a17610a1f0 | |||
d2f52f4c7f | |||
b2fb0c10ca | |||
ea57c523b7 | |||
216f55c571 | |||
9329036920 | |||
5d04dd4ef8 | |||
82ab3e9ba9 | |||
c72e7d9363 |
@ -9,6 +9,12 @@ rust-version = "1.73"
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
# DO NOT MERGE ME WITH THIS PLEASE
|
||||
#
|
||||
# FOR THE LOVE OF GOD DON'T HIT APPROVE PR WITHOUT ASKING ME TO REMOVE THIS
|
||||
[package.metadata.wasm-pack.profile.release]
|
||||
wasm-opt = false
|
||||
|
||||
[dependencies]
|
||||
bson = { version = "2.13.0", features = ["uuid-1", "chrono"] }
|
||||
data-encoding = "2.6.0"
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! The executor for the AST.
|
||||
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
use std::{cmp::Ordering, path::PathBuf, sync::Arc};
|
||||
|
||||
use annotations::AnnotationScope;
|
||||
use anyhow::Result;
|
||||
@ -43,6 +43,7 @@ use crate::{
|
||||
BodyItem, Expr, FunctionExpression, ImportSelector, ItemVisibility, Node, NodeRef, NonCodeValue,
|
||||
Program as AstProgram, TagDeclarator, TagNode,
|
||||
},
|
||||
refgraph::extract_refgraph,
|
||||
settings::types::UnitLength,
|
||||
source_range::{ModuleId, SourceRange},
|
||||
std::{args::Arg, StdLib},
|
||||
|
@ -66,6 +66,7 @@ pub mod lint;
|
||||
mod log;
|
||||
mod lsp;
|
||||
mod parsing;
|
||||
mod refgraph;
|
||||
mod settings;
|
||||
#[cfg(test)]
|
||||
mod simulation_tests;
|
||||
|
296
src/wasm-lib/kcl/src/refgraph/mod.rs
Normal file
296
src/wasm-lib/kcl/src/refgraph/mod.rs
Normal file
@ -0,0 +1,296 @@
|
||||
mod walk;
|
||||
|
||||
use crate::{
|
||||
parsing::ast::types,
|
||||
walk::{Node, Visitable},
|
||||
FormatOptions,
|
||||
};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
///
|
||||
type Declaration<'tree> = (Vec<Node<'tree>>, &'tree types::Identifier);
|
||||
|
||||
///
|
||||
type Reference<'tree> = (Vec<Node<'tree>>, &'tree types::Identifier);
|
||||
|
||||
///
|
||||
type RefEdge<'tree> = (Option<Declaration<'tree>>, Reference<'tree>);
|
||||
|
||||
///
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Scope<'tree> {
|
||||
///
|
||||
pub declarations: Vec<Declaration<'tree>>,
|
||||
|
||||
///
|
||||
pub references: Vec<Reference<'tree>>,
|
||||
|
||||
///
|
||||
pub children: Vec<Scope<'tree>>,
|
||||
}
|
||||
|
||||
impl<'tree> Scope<'tree> {
|
||||
pub fn new() -> Self {
|
||||
Scope {
|
||||
declarations: vec![],
|
||||
references: vec![],
|
||||
children: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn edges(&self) -> Vec<RefEdge> {
|
||||
let mut edges = Vec::<RefEdge>::new();
|
||||
let mut unmatched_refs = self.references.clone();
|
||||
|
||||
for child in &self.children {
|
||||
for (declaration, (ref_node, ref_id)) in child.edges() {
|
||||
match declaration {
|
||||
Some((decl_node, decl_id)) => {
|
||||
edges.push((Some((decl_node.clone(), decl_id)), (ref_node.clone(), ref_id)));
|
||||
}
|
||||
None => {
|
||||
unmatched_refs.push((ref_node.clone(), ref_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (ref_node, ref_id) in unmatched_refs.into_iter() {
|
||||
edges.push((
|
||||
self.declarations
|
||||
.iter()
|
||||
.filter(|(_, decl_id)| decl_id.name == ref_id.name)
|
||||
.cloned()
|
||||
.next(),
|
||||
(ref_node.clone(), ref_id),
|
||||
));
|
||||
}
|
||||
|
||||
edges
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ScopeVisitor<'tree> {
|
||||
scope: Arc<Mutex<Scope<'tree>>>,
|
||||
}
|
||||
|
||||
impl<'tree> ScopeVisitor<'tree> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
scope: Arc::new(Mutex::new(Scope::new())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tree> ScopeVisitor<'tree> {
|
||||
fn visit(&self, path: Vec<Node<'tree>>, node: Node<'tree>) -> Result<bool, std::convert::Infallible> {
|
||||
if let Node::Program(program) = node {
|
||||
let csv = ScopeVisitor::new();
|
||||
for child in node.children() {
|
||||
let mut path = path.clone();
|
||||
path.push(node.clone());
|
||||
csv.clone().visit(path, child.clone())?;
|
||||
}
|
||||
|
||||
self.scope
|
||||
.lock()
|
||||
.unwrap()
|
||||
.children
|
||||
.push(csv.scope.lock().unwrap().clone());
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
let mut path = path.clone();
|
||||
path.push(node.clone());
|
||||
|
||||
match node {
|
||||
Node::VariableDeclaration(vd) => {
|
||||
self.scope
|
||||
.lock()
|
||||
.unwrap()
|
||||
.declarations
|
||||
.push((vec![node.clone()], &vd.declaration.id));
|
||||
|
||||
let node: Node = (&vd.declaration.init).into();
|
||||
self.clone().visit(path, node)?;
|
||||
}
|
||||
Node::Identifier(id) => {
|
||||
self.scope.lock().unwrap().references.push((vec![node.clone()], &id));
|
||||
}
|
||||
_ => {
|
||||
for child in node.children() {
|
||||
self.visit(path.clone(), child.clone())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_refgraph<'a, 'tree>(
|
||||
program: types::NodeRef<'tree, types::Program>,
|
||||
) -> Result<Scope<'tree>, std::convert::Infallible> {
|
||||
let sv = ScopeVisitor::new();
|
||||
let node: Node = (program).into();
|
||||
|
||||
for child in node.children() {
|
||||
sv.clone().visit(vec![node.clone()], child.clone())?;
|
||||
}
|
||||
|
||||
let x = sv.scope.lock().unwrap().clone();
|
||||
Ok(x)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
macro_rules! kcl {
|
||||
( $kcl:expr ) => {{
|
||||
$crate::parsing::top_level_parse($kcl).unwrap()
|
||||
}};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_edges() {
|
||||
let program = kcl!(
|
||||
"
|
||||
const foo = 1
|
||||
const bar = foo + 1
|
||||
|
||||
fn myfn = () => {
|
||||
const foo = 2
|
||||
sin(foo)
|
||||
}
|
||||
"
|
||||
);
|
||||
|
||||
let refgraph = extract_refgraph(&program).unwrap();
|
||||
|
||||
let edges = refgraph.edges().into_iter().collect::<Vec<_>>();
|
||||
assert_eq!(edges.len(), 3);
|
||||
|
||||
// sin is a global so we are chilin
|
||||
let (decl, (_ref_node, ref_id)) = edges.get(2).unwrap();
|
||||
assert_eq!("sin", ref_id.name);
|
||||
assert!(decl.is_none());
|
||||
|
||||
// myfn's foo
|
||||
let (decl, (_ref_node, ref_id)) = edges.get(1).unwrap();
|
||||
assert_eq!("foo", ref_id.name);
|
||||
assert!(decl.is_some());
|
||||
// todo: check this is actually refering to the parent scope foo
|
||||
|
||||
let (Some((decl_nodes, _)), _) = edges.get(1).unwrap() else {
|
||||
panic!("not some");
|
||||
};
|
||||
|
||||
assert!(decl_nodes[0].ptr_eq((&program.body[0]).into()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_simple() {
|
||||
let program = kcl!(
|
||||
"
|
||||
const foo = 1
|
||||
const bar = foo + 1
|
||||
|
||||
fn myfn = () => {
|
||||
const foo = 2
|
||||
sin(foo)
|
||||
}
|
||||
"
|
||||
);
|
||||
|
||||
let refgraph = extract_refgraph(&program).unwrap();
|
||||
|
||||
assert_eq!(3, refgraph.declarations.len());
|
||||
assert_eq!(
|
||||
&["foo", "bar", "myfn"],
|
||||
refgraph
|
||||
.declarations
|
||||
.iter()
|
||||
.map(|(_, id)| id.name.as_str())
|
||||
.collect::<Vec<_>>()
|
||||
.as_slice()
|
||||
);
|
||||
|
||||
assert_eq!(1, refgraph.references.len());
|
||||
assert_eq!(
|
||||
&["foo"],
|
||||
refgraph
|
||||
.references
|
||||
.iter()
|
||||
.map(|(_, id)| id.name.as_str())
|
||||
.collect::<Vec<_>>()
|
||||
.as_slice()
|
||||
);
|
||||
|
||||
assert_eq!(1, refgraph.children.len());
|
||||
let myfn = refgraph.children.first().unwrap();
|
||||
|
||||
assert_eq!(myfn.declarations.len(), 1);
|
||||
assert_eq!(
|
||||
&["foo"],
|
||||
myfn.declarations
|
||||
.iter()
|
||||
.map(|(_, id)| id.name.as_str())
|
||||
.collect::<Vec<_>>()
|
||||
.as_slice()
|
||||
);
|
||||
|
||||
assert_eq!(myfn.references.len(), 2);
|
||||
assert_eq!(
|
||||
&["sin", "foo"],
|
||||
myfn.references
|
||||
.iter()
|
||||
.map(|(_, id)| id.name.as_str())
|
||||
.collect::<Vec<_>>()
|
||||
.as_slice()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ast_eq() {
|
||||
const program_str: &str = "\
|
||||
foo = 1
|
||||
bar = 2
|
||||
|
||||
fn myfn() {
|
||||
sin(foo)
|
||||
}
|
||||
|
||||
baz = myfn()
|
||||
";
|
||||
|
||||
let program = kcl!(program_str);
|
||||
|
||||
// if this fails its because the code above is no longer of the
|
||||
// style of the day
|
||||
assert_eq!(program_str, program.recast(&FormatOptions::new(), 0));
|
||||
|
||||
let refgraph = extract_refgraph(&program).unwrap();
|
||||
let edges = refgraph.edges().into_iter().collect::<Vec<_>>();
|
||||
|
||||
// lets say "foo" changed here
|
||||
let tainted_node = program.body.get(0).unwrap();
|
||||
|
||||
let program_min = &program; // minimize_ast(&program, &[tainted_node]);
|
||||
assert_eq!(
|
||||
"\
|
||||
foo = 1
|
||||
bar = 2
|
||||
|
||||
fn myfn() {
|
||||
sin(foo)
|
||||
}
|
||||
|
||||
baz = myfn()
|
||||
",
|
||||
program_min.recast(&FormatOptions::new(), 0)
|
||||
);
|
||||
}
|
||||
}
|
1
src/wasm-lib/kcl/src/refgraph/walk.rs
Normal file
1
src/wasm-lib/kcl/src/refgraph/walk.rs
Normal file
@ -0,0 +1 @@
|
||||
|
@ -41,6 +41,46 @@ pub enum Node<'a> {
|
||||
KclNone(&'a types::KclNone),
|
||||
}
|
||||
|
||||
impl Node<'_> {
|
||||
///
|
||||
pub fn ptr_eq(&self, other: Node) -> bool {
|
||||
unsafe { std::ptr::eq(self.ptr(), other.ptr()) }
|
||||
}
|
||||
|
||||
unsafe fn ptr(&self) -> *const () {
|
||||
match self {
|
||||
Node::Program(n) => *n as *const _ as *const (),
|
||||
Node::ImportStatement(n) => *n as *const _ as *const (),
|
||||
Node::ExpressionStatement(n) => *n as *const _ as *const (),
|
||||
Node::VariableDeclaration(n) => *n as *const _ as *const (),
|
||||
Node::ReturnStatement(n) => *n as *const _ as *const (),
|
||||
Node::VariableDeclarator(n) => *n as *const _ as *const (),
|
||||
Node::Literal(n) => *n as *const _ as *const (),
|
||||
Node::TagDeclarator(n) => *n as *const _ as *const (),
|
||||
Node::Identifier(n) => *n as *const _ as *const (),
|
||||
Node::BinaryExpression(n) => *n as *const _ as *const (),
|
||||
Node::FunctionExpression(n) => *n as *const _ as *const (),
|
||||
Node::CallExpression(n) => *n as *const _ as *const (),
|
||||
Node::CallExpressionKw(n) => *n as *const _ as *const (),
|
||||
Node::PipeExpression(n) => *n as *const _ as *const (),
|
||||
Node::PipeSubstitution(n) => *n as *const _ as *const (),
|
||||
Node::ArrayExpression(n) => *n as *const _ as *const (),
|
||||
Node::ArrayRangeExpression(n) => *n as *const _ as *const (),
|
||||
Node::ObjectExpression(n) => *n as *const _ as *const (),
|
||||
Node::MemberExpression(n) => *n as *const _ as *const (),
|
||||
Node::UnaryExpression(n) => *n as *const _ as *const (),
|
||||
Node::Parameter(p) => *p as *const _ as *const (),
|
||||
Node::ObjectProperty(n) => *n as *const _ as *const (),
|
||||
Node::IfExpression(n) => *n as *const _ as *const (),
|
||||
Node::ElseIf(n) => *n as *const _ as *const (),
|
||||
Node::KclNone(n) => *n as *const _ as *const (),
|
||||
Node::LabelledExpression(n) => *n as *const _ as *const (),
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn ptr_eq(&self, other: Node<'_>) -> bool {}
|
||||
}
|
||||
|
||||
impl Node<'_> {
|
||||
/// Return the digest of the [Node], pulling the underlying Digest from
|
||||
/// the AST types.
|
||||
|
@ -3,4 +3,5 @@ mod ast_visitor;
|
||||
mod ast_walk;
|
||||
|
||||
pub use ast_node::Node;
|
||||
pub use ast_visitor::{Visitable, Visitor};
|
||||
pub use ast_walk::walk;
|
||||
|
Reference in New Issue
Block a user