Remove annotations from non-code-meta and store them in nodes (#5360)

Signed-off-by: Nick Cameron <nrc@ncameron.org>
This commit is contained in:
Nick Cameron
2025-02-13 16:17:09 +13:00
committed by GitHub
parent 0874891dd0
commit 1f405b9327
16 changed files with 547 additions and 584 deletions

View File

@ -60,7 +60,8 @@ export class KclManager {
nonCodeNodes: {}, nonCodeNodes: {},
startNodes: [], startNodes: [],
}, },
trivia: [], innerAttrs: [],
outerAttrs: [],
} }
private _execState: ExecState = emptyExecState() private _execState: ExecState = emptyExecState()
private _variables: VariableMap = {} private _variables: VariableMap = {}
@ -255,7 +256,8 @@ export class KclManager {
nonCodeNodes: {}, nonCodeNodes: {},
startNodes: [], startNodes: [],
}, },
trivia: [], innerAttrs: [],
outerAttrs: [],
} }
} }

View File

@ -134,7 +134,7 @@ describe('Testing findUniqueName', () => {
start: 0, start: 0,
end: 0, end: 0,
moduleId: 0, moduleId: 0,
trivia: [], outerAttrs: [],
}, },
{ {
type: 'Identifier', type: 'Identifier',
@ -142,7 +142,7 @@ describe('Testing findUniqueName', () => {
start: 0, start: 0,
end: 0, end: 0,
moduleId: 0, moduleId: 0,
trivia: [], outerAttrs: [],
}, },
{ {
type: 'Identifier', type: 'Identifier',
@ -150,7 +150,7 @@ describe('Testing findUniqueName', () => {
start: 0, start: 0,
end: 0, end: 0,
moduleId: 0, moduleId: 0,
trivia: [], outerAttrs: [],
}, },
{ {
type: 'Identifier', type: 'Identifier',
@ -158,7 +158,7 @@ describe('Testing findUniqueName', () => {
start: 0, start: 0,
end: 0, end: 0,
moduleId: 0, moduleId: 0,
trivia: [], outerAttrs: [],
}, },
{ {
type: 'Identifier', type: 'Identifier',
@ -166,7 +166,7 @@ describe('Testing findUniqueName', () => {
start: 0, start: 0,
end: 0, end: 0,
moduleId: 0, moduleId: 0,
trivia: [], outerAttrs: [],
}, },
{ {
type: 'Identifier', type: 'Identifier',
@ -174,7 +174,7 @@ describe('Testing findUniqueName', () => {
start: 0, start: 0,
end: 0, end: 0,
moduleId: 0, moduleId: 0,
trivia: [], outerAttrs: [],
}, },
{ {
type: 'Identifier', type: 'Identifier',
@ -182,7 +182,7 @@ describe('Testing findUniqueName', () => {
start: 0, start: 0,
end: 0, end: 0,
moduleId: 0, moduleId: 0,
trivia: [], outerAttrs: [],
}, },
{ {
type: 'Identifier', type: 'Identifier',
@ -190,7 +190,7 @@ describe('Testing findUniqueName', () => {
start: 0, start: 0,
end: 0, end: 0,
moduleId: 0, moduleId: 0,
trivia: [], outerAttrs: [],
}, },
{ {
type: 'Identifier', type: 'Identifier',
@ -198,7 +198,7 @@ describe('Testing findUniqueName', () => {
start: 0, start: 0,
end: 0, end: 0,
moduleId: 0, moduleId: 0,
trivia: [], outerAttrs: [],
}, },
] satisfies Node<Identifier>[]), ] satisfies Node<Identifier>[]),
'yo', 'yo',
@ -217,7 +217,8 @@ describe('Testing addSketchTo', () => {
end: 0, end: 0,
moduleId: 0, moduleId: 0,
nonCodeMeta: { nonCodeNodes: {}, startNodes: [] }, nonCodeMeta: { nonCodeNodes: {}, startNodes: [] },
trivia: [], innerAttrs: [],
outerAttrs: [],
}, },
'yz' 'yz'
) )

View File

@ -278,7 +278,7 @@ export function mutateObjExpProp(
start: 0, start: 0,
end: 0, end: 0,
moduleId: 0, moduleId: 0,
trivia: [], outerAttrs: [],
}) })
} }
} }
@ -891,7 +891,7 @@ export function createLiteral(value: LiteralValue | number): Node<Literal> {
moduleId: 0, moduleId: 0,
value, value,
raw, raw,
trivia: [], outerAttrs: [],
} }
} }
@ -901,7 +901,7 @@ export function createTagDeclarator(value: string): Node<TagDeclarator> {
start: 0, start: 0,
end: 0, end: 0,
moduleId: 0, moduleId: 0,
trivia: [], outerAttrs: [],
value, value,
} }
@ -913,7 +913,7 @@ export function createIdentifier(name: string): Node<Identifier> {
start: 0, start: 0,
end: 0, end: 0,
moduleId: 0, moduleId: 0,
trivia: [], outerAttrs: [],
name, name,
} }
@ -925,7 +925,7 @@ export function createPipeSubstitution(): Node<PipeSubstitution> {
start: 0, start: 0,
end: 0, end: 0,
moduleId: 0, moduleId: 0,
trivia: [], outerAttrs: [],
} }
} }
@ -938,13 +938,13 @@ export function createCallExpressionStdLib(
start: 0, start: 0,
end: 0, end: 0,
moduleId: 0, moduleId: 0,
trivia: [], outerAttrs: [],
callee: { callee: {
type: 'Identifier', type: 'Identifier',
start: 0, start: 0,
end: 0, end: 0,
moduleId: 0, moduleId: 0,
trivia: [], outerAttrs: [],
name, name,
}, },
@ -962,13 +962,13 @@ export function createCallExpressionStdLibKw(
start: 0, start: 0,
end: 0, end: 0,
moduleId: 0, moduleId: 0,
trivia: [], outerAttrs: [],
callee: { callee: {
type: 'Identifier', type: 'Identifier',
start: 0, start: 0,
end: 0, end: 0,
moduleId: 0, moduleId: 0,
trivia: [], outerAttrs: [],
name, name,
}, },
@ -986,13 +986,13 @@ export function createCallExpression(
start: 0, start: 0,
end: 0, end: 0,
moduleId: 0, moduleId: 0,
trivia: [], outerAttrs: [],
callee: { callee: {
type: 'Identifier', type: 'Identifier',
start: 0, start: 0,
end: 0, end: 0,
moduleId: 0, moduleId: 0,
trivia: [], outerAttrs: [],
name, name,
}, },
@ -1008,7 +1008,7 @@ export function createArrayExpression(
start: 0, start: 0,
end: 0, end: 0,
moduleId: 0, moduleId: 0,
trivia: [], outerAttrs: [],
nonCodeMeta: nonCodeMetaEmpty(), nonCodeMeta: nonCodeMetaEmpty(),
elements, elements,
@ -1023,7 +1023,7 @@ export function createPipeExpression(
start: 0, start: 0,
end: 0, end: 0,
moduleId: 0, moduleId: 0,
trivia: [], outerAttrs: [],
body, body,
nonCodeMeta: nonCodeMetaEmpty(), nonCodeMeta: nonCodeMetaEmpty(),
@ -1041,14 +1041,14 @@ export function createVariableDeclaration(
start: 0, start: 0,
end: 0, end: 0,
moduleId: 0, moduleId: 0,
trivia: [], outerAttrs: [],
declaration: { declaration: {
type: 'VariableDeclarator', type: 'VariableDeclarator',
start: 0, start: 0,
end: 0, end: 0,
moduleId: 0, moduleId: 0,
trivia: [], outerAttrs: [],
id: createIdentifier(varName), id: createIdentifier(varName),
init, init,
@ -1066,7 +1066,7 @@ export function createObjectExpression(properties: {
start: 0, start: 0,
end: 0, end: 0,
moduleId: 0, moduleId: 0,
trivia: [], outerAttrs: [],
nonCodeMeta: nonCodeMetaEmpty(), nonCodeMeta: nonCodeMetaEmpty(),
properties: Object.entries(properties).map(([key, value]) => ({ properties: Object.entries(properties).map(([key, value]) => ({
@ -1074,7 +1074,7 @@ export function createObjectExpression(properties: {
start: 0, start: 0,
end: 0, end: 0,
moduleId: 0, moduleId: 0,
trivia: [], outerAttrs: [],
key: createIdentifier(key), key: createIdentifier(key),
value, value,
@ -1091,7 +1091,7 @@ export function createUnaryExpression(
start: 0, start: 0,
end: 0, end: 0,
moduleId: 0, moduleId: 0,
trivia: [], outerAttrs: [],
operator, operator,
argument, argument,
@ -1108,7 +1108,7 @@ export function createBinaryExpression([left, operator, right]: [
start: 0, start: 0,
end: 0, end: 0,
moduleId: 0, moduleId: 0,
trivia: [], outerAttrs: [],
operator, operator,
left, left,

View File

@ -1945,7 +1945,8 @@ export const updateStartProfileAtArgs: SketchLineHelper['updateArgs'] = ({
startNodes: [], startNodes: [],
nonCodeNodes: [], nonCodeNodes: [],
}, },
trivia: [], innerAttrs: [],
outerAttrs: [],
}, },
pathToNode, pathToNode,
} }
@ -2527,7 +2528,7 @@ function addTagKw(): addTagFn {
start: callExpr.node.start, start: callExpr.node.start,
end: callExpr.node.end, end: callExpr.node.end,
moduleId: callExpr.node.moduleId, moduleId: callExpr.node.moduleId,
trivia: callExpr.node.trivia, outerAttrs: callExpr.node.outerAttrs,
}) })
} }

View File

@ -5,14 +5,18 @@ use kittycad_modeling_cmds::coord::{System, KITTYCAD, OPENGL, VULKAN};
use crate::{ use crate::{
errors::KclErrorDetails, errors::KclErrorDetails,
execution::kcl_value::{UnitAngle, UnitLen}, execution::kcl_value::{UnitAngle, UnitLen},
parsing::ast::types::{Expr, Node, NonCodeValue, ObjectProperty}, parsing::ast::types::{Annotation, Expr, Node, ObjectProperty},
KclError, SourceRange, KclError, SourceRange,
}; };
/// Annotations which should cause re-execution if they change.
pub(super) const SIGNIFICANT_ATTRS: [&str; 2] = [SETTINGS, NO_PRELUDE];
pub(crate) const SETTINGS: &str = "settings"; pub(crate) const SETTINGS: &str = "settings";
pub(crate) const SETTINGS_UNIT_LENGTH: &str = "defaultLengthUnit"; pub(crate) const SETTINGS_UNIT_LENGTH: &str = "defaultLengthUnit";
pub(crate) const SETTINGS_UNIT_ANGLE: &str = "defaultAngleUnit"; pub(crate) const SETTINGS_UNIT_ANGLE: &str = "defaultAngleUnit";
pub(super) const NO_PRELUDE: &str = "no_prelude"; pub(super) const NO_PRELUDE: &str = "no_prelude";
pub(super) const IMPORT_FORMAT: &str = "format"; pub(super) const IMPORT_FORMAT: &str = "format";
pub(super) const IMPORT_FORMAT_VALUES: [&str; 9] = ["fbx", "gltf", "glb", "obj", "ply", "sldprt", "stp", "step", "stl"]; pub(super) const IMPORT_FORMAT_VALUES: [&str; 9] = ["fbx", "gltf", "glb", "obj", "ply", "sldprt", "stp", "step", "stl"];
pub(super) const IMPORT_COORDS: &str = "coords"; pub(super) const IMPORT_COORDS: &str = "coords";
@ -25,35 +29,24 @@ pub(super) enum AnnotationScope {
Module, Module,
} }
pub(super) fn expect_properties<'a>( pub(super) fn is_significant(attr: &&Node<Annotation>) -> bool {
for_key: &'static str, match attr.name() {
annotation: &'a NonCodeValue, Some(name) => SIGNIFICANT_ATTRS.contains(&name),
source_range: SourceRange, None => true,
) -> Result<&'a [Node<ObjectProperty>], KclError> {
match annotation {
NonCodeValue::Annotation { name, properties } => {
assert_eq!(name.as_ref().unwrap().name, for_key);
Ok(&**properties.as_ref().ok_or_else(|| {
KclError::Semantic(KclErrorDetails {
message: format!("Empty `{for_key}` annotation"),
source_ranges: vec![source_range],
})
})?)
}
_ => unreachable!(),
} }
} }
pub(super) fn unnamed_properties<'a>( pub(super) fn expect_properties<'a>(
annotations: impl Iterator<Item = &'a NonCodeValue>, for_key: &'static str,
) -> Option<&'a [Node<ObjectProperty>]> { annotation: &'a Node<Annotation>,
for annotation in annotations { ) -> Result<&'a [Node<ObjectProperty>], KclError> {
if let NonCodeValue::Annotation { name: None, properties } = annotation { assert_eq!(annotation.name().unwrap(), for_key);
return properties.as_deref(); Ok(&**annotation.properties.as_ref().ok_or_else(|| {
} KclError::Semantic(KclErrorDetails {
} message: format!("Empty `{for_key}` annotation"),
source_ranges: vec![annotation.as_source_range()],
None })
})?)
} }
pub(super) fn expect_ident(expr: &Expr) -> Result<&str, KclError> { pub(super) fn expect_ident(expr: &Expr) -> Result<&str, KclError> {

View File

@ -6,8 +6,8 @@ use itertools::{EitherOrBoth, Itertools};
use tokio::sync::RwLock; use tokio::sync::RwLock;
use crate::{ use crate::{
execution::{memory::ProgramMemory, ExecState, ExecutorSettings}, execution::{annotations, memory::ProgramMemory, ExecState, ExecutorSettings},
parsing::ast::types::{Node, NonCodeValue, Program}, parsing::ast::types::{Annotation, Node, Program},
walk::Node as WalkNode, walk::Node as WalkNode,
}; };
@ -91,7 +91,7 @@ pub(super) enum CacheResult {
/// Returns `None` when there are no changes to the program, i.e. it is /// Returns `None` when there are no changes to the program, i.e. it is
/// fully cached. /// fully cached.
pub(super) async fn get_changed_program(old: CacheInformation<'_>, new: CacheInformation<'_>) -> CacheResult { pub(super) async fn get_changed_program(old: CacheInformation<'_>, new: CacheInformation<'_>) -> CacheResult {
let mut try_reapply_settings = false; let mut reapply_settings = false;
// If the settings are different we might need to bust the cache. // If the settings are different we might need to bust the cache.
// We specifically do this before checking if they are the exact same. // We specifically do this before checking if they are the exact same.
@ -107,13 +107,13 @@ pub(super) async fn get_changed_program(old: CacheInformation<'_>, new: CacheInf
// If anything else is different we may not need to re-execute, but rather just // If anything else is different we may not need to re-execute, but rather just
// run the settings again. // run the settings again.
try_reapply_settings = true; reapply_settings = true;
} }
// If the ASTs are the EXACT same we return None. // If the ASTs are the EXACT same we return None.
// We don't even need to waste time computing the digests. // We don't even need to waste time computing the digests.
if old.ast == new.ast { if old.ast == new.ast {
return CacheResult::NoAction(try_reapply_settings); return CacheResult::NoAction(reapply_settings);
} }
// We have to clone just because the digests are stored inline :-( // We have to clone just because the digests are stored inline :-(
@ -127,23 +127,27 @@ pub(super) async fn get_changed_program(old: CacheInformation<'_>, new: CacheInf
// Check if the digest is the same. // Check if the digest is the same.
if old_ast.digest == new_ast.digest { if old_ast.digest == new_ast.digest {
return CacheResult::NoAction(try_reapply_settings); return CacheResult::NoAction(reapply_settings);
} }
// Check if the annotations are different. // Check if the annotations are different.
if !old_ast.annotations().zip_longest(new_ast.annotations()).all(|pair| { if !old_ast
.inner_attrs
.iter()
.filter(annotations::is_significant)
.zip_longest(new_ast.inner_attrs.iter().filter(annotations::is_significant))
.all(|pair| {
match pair { match pair {
EitherOrBoth::Both(old, new) => { EitherOrBoth::Both(old, new) => {
// Compare annotations, ignoring source ranges. Digests must // Compare annotations, ignoring source ranges. Digests must
// have been computed before this. // have been computed before this.
match (&old.value, &new.value) { let Annotation { name, properties, .. } = &old.inner;
( let Annotation {
NonCodeValue::Annotation { name, properties },
NonCodeValue::Annotation {
name: new_name, name: new_name,
properties: new_properties, properties: new_properties,
}, ..
) => { } = &new.inner;
name.as_ref().map(|n| n.digest) == new_name.as_ref().map(|n| n.digest) name.as_ref().map(|n| n.digest) == new_name.as_ref().map(|n| n.digest)
&& properties && properties
.as_ref() .as_ref()
@ -154,10 +158,8 @@ pub(super) async fn get_changed_program(old: CacheInformation<'_>, new: CacheInf
} }
_ => false, _ => false,
} }
} })
_ => false, {
}
}) {
// If any of the annotations are different at the beginning of the // If any of the annotations are different at the beginning of the
// program, it's likely the settings, and we have to bust the cache and // program, it's likely the settings, and we have to bust the cache and
// re-execute the whole thing. // re-execute the whole thing.
@ -169,12 +171,7 @@ pub(super) async fn get_changed_program(old: CacheInformation<'_>, new: CacheInf
} }
// Check if the changes were only to Non-code areas, like comments or whitespace. // Check if the changes were only to Non-code areas, like comments or whitespace.
let (clear_scene, program) = generate_changed_program(old_ast, new_ast); generate_changed_program(old_ast, new_ast, reapply_settings)
CacheResult::ReExecute {
clear_scene,
reapply_settings: try_reapply_settings,
program,
}
} }
/// Force-generate a new CacheResult, even if one shouldn't be made. The /// Force-generate a new CacheResult, even if one shouldn't be made. The
@ -183,7 +180,7 @@ pub(super) async fn get_changed_program(old: CacheInformation<'_>, new: CacheInf
/// how we construct a new [CacheResult]. /// how we construct a new [CacheResult].
/// ///
/// Digests *must* be computed before calling this. /// Digests *must* be computed before calling this.
fn generate_changed_program(old_ast: Node<Program>, mut new_ast: Node<Program>) -> (bool, Node<Program>) { fn generate_changed_program(old_ast: Node<Program>, mut new_ast: Node<Program>, reapply_settings: bool) -> CacheResult {
if !old_ast.body.iter().zip(new_ast.body.iter()).all(|(old, new)| { if !old_ast.body.iter().zip(new_ast.body.iter()).all(|(old, new)| {
let old_node: WalkNode = old.into(); let old_node: WalkNode = old.into();
let new_node: WalkNode = new.into(); let new_node: WalkNode = new.into();
@ -194,7 +191,11 @@ fn generate_changed_program(old_ast: Node<Program>, mut new_ast: Node<Program>)
// means a single insertion or deletion will result in a cache // means a single insertion or deletion will result in a cache
// bust. // bust.
return (true, new_ast); return CacheResult::ReExecute {
clear_scene: true,
reapply_settings,
program: new_ast,
};
} }
// otherwise the overlapping section of the ast bodies matches. // otherwise the overlapping section of the ast bodies matches.
@ -210,7 +211,11 @@ fn generate_changed_program(old_ast: Node<Program>, mut new_ast: Node<Program>)
// supporting that. // supporting that.
// Cache bust time. // Cache bust time.
(true, new_ast) CacheResult::ReExecute {
clear_scene: true,
reapply_settings,
program: new_ast,
}
} }
std::cmp::Ordering::Greater => { std::cmp::Ordering::Greater => {
// the new AST is longer than the old AST, which means // the new AST is longer than the old AST, which means
@ -222,7 +227,11 @@ fn generate_changed_program(old_ast: Node<Program>, mut new_ast: Node<Program>)
new_ast.body = new_ast.body[old_ast.body.len()..].to_owned(); new_ast.body = new_ast.body[old_ast.body.len()..].to_owned();
(false, new_ast) CacheResult::ReExecute {
clear_scene: false,
reapply_settings,
program: new_ast,
}
} }
std::cmp::Ordering::Equal => { std::cmp::Ordering::Equal => {
// currently unreachable, but let's pretend like the code // currently unreachable, but let's pretend like the code
@ -234,9 +243,7 @@ fn generate_changed_program(old_ast: Node<Program>, mut new_ast: Node<Program>)
// so but i think many things. This def needs to change // so but i think many things. This def needs to change
// when the code above changes. // when the code above changes.
new_ast.body = vec![]; CacheResult::NoAction(reapply_settings)
(false, new_ast)
} }
} }
} }
@ -368,8 +375,10 @@ shell(firstSketch, faces = ['end'], thickness = 0.25)"#;
} }
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_get_changed_program_same_code_changed_code_comments() { async fn test_get_changed_program_same_code_changed_code_comments_attrs() {
let old = r#" // Removed the end face for the extrusion. let old = r#"@foo(whatever = whatever)
@bar
// Removed the end face for the extrusion.
firstSketch = startSketchOn('XY') firstSketch = startSketchOn('XY')
|> startProfileAt([-12, 12], %) |> startProfileAt([-12, 12], %)
|> line(end = [24, 0]) |> line(end = [24, 0])
@ -381,7 +390,9 @@ firstSketch = startSketchOn('XY')
// Remove the end face for the extrusion. // Remove the end face for the extrusion.
shell(firstSketch, faces = ['end'], thickness = 0.25) "#; shell(firstSketch, faces = ['end'], thickness = 0.25) "#;
let new = r#"// Remove the end face for the extrusion. let new = r#"@foo(whatever = 42)
@baz
// Remove the end face for the extrusion.
firstSketch = startSketchOn('XY') firstSketch = startSketchOn('XY')
|> startProfileAt([-12, 12], %) |> startProfileAt([-12, 12], %)
|> line(end = [24, 0]) |> line(end = [24, 0])

View File

@ -17,9 +17,9 @@ use crate::{
}, },
modules::{ModuleId, ModulePath, ModuleRepr}, modules::{ModuleId, ModulePath, ModuleRepr},
parsing::ast::types::{ parsing::ast::types::{
ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem, CallExpression, Annotation, ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem,
CallExpressionKw, Expr, FunctionExpression, IfExpression, ImportPath, ImportSelector, ItemVisibility, CallExpression, CallExpressionKw, Expr, FunctionExpression, IfExpression, ImportPath, ImportSelector,
LiteralIdentifier, LiteralValue, MemberExpression, MemberObject, Node, NodeRef, NonCodeNode, NonCodeValue, ItemVisibility, LiteralIdentifier, LiteralValue, MemberExpression, MemberObject, Node, NodeRef,
ObjectExpression, PipeExpression, Program, TagDeclarator, UnaryExpression, UnaryOperator, ObjectExpression, PipeExpression, Program, TagDeclarator, UnaryExpression, UnaryOperator,
}, },
source_range::SourceRange, source_range::SourceRange,
@ -37,37 +37,36 @@ enum StatementKind<'a> {
impl ExecutorContext { impl ExecutorContext {
async fn handle_annotations( async fn handle_annotations(
&self, &self,
annotations: impl Iterator<Item = (&NonCodeValue, SourceRange)>, annotations: impl Iterator<Item = &Node<Annotation>>,
scope: annotations::AnnotationScope, scope: annotations::AnnotationScope,
exec_state: &mut ExecState, exec_state: &mut ExecState,
) -> Result<bool, KclError> { ) -> Result<bool, KclError> {
let mut no_prelude = false; let mut no_prelude = false;
for (annotation, source_range) in annotations { for annotation in annotations {
if annotation.annotation_name() == Some(annotations::SETTINGS) { if annotation.name() == Some(annotations::SETTINGS) {
if scope == annotations::AnnotationScope::Module { if scope == annotations::AnnotationScope::Module {
let old_units = exec_state.length_unit(); let old_units = exec_state.length_unit();
exec_state exec_state.mod_local.settings.update_from_annotation(annotation)?;
.mod_local
.settings
.update_from_annotation(annotation, source_range)?;
let new_units = exec_state.length_unit(); let new_units = exec_state.length_unit();
if !self.engine.execution_kind().is_isolated() && old_units != new_units { if !self.engine.execution_kind().is_isolated() && old_units != new_units {
self.engine.set_units(new_units.into(), source_range).await?; self.engine
.set_units(new_units.into(), annotation.as_source_range())
.await?;
} }
} else { } else {
return Err(KclError::Semantic(KclErrorDetails { return Err(KclError::Semantic(KclErrorDetails {
message: "Settings can only be modified at the top level scope of a file".to_owned(), message: "Settings can only be modified at the top level scope of a file".to_owned(),
source_ranges: vec![source_range], source_ranges: vec![annotation.as_source_range()],
})); }));
} }
} }
if annotation.annotation_name() == Some(annotations::NO_PRELUDE) { if annotation.name() == Some(annotations::NO_PRELUDE) {
if scope == annotations::AnnotationScope::Module { if scope == annotations::AnnotationScope::Module {
no_prelude = true; no_prelude = true;
} else { } else {
return Err(KclError::Semantic(KclErrorDetails { return Err(KclError::Semantic(KclErrorDetails {
message: "Prelude can only be skipped at the top level scope of a file".to_owned(), message: "Prelude can only be skipped at the top level scope of a file".to_owned(),
source_ranges: vec![source_range], source_ranges: vec![annotation.as_source_range()],
})); }));
} }
} }
@ -87,11 +86,7 @@ impl ExecutorContext {
if body_type == BodyType::Root { if body_type == BodyType::Root {
let _no_prelude = self let _no_prelude = self
.handle_annotations( .handle_annotations(
program program.inner_attrs.iter(),
.non_code_meta
.start_nodes
.iter()
.filter_map(|n| n.annotation().map(|result| (result, n.as_source_range()))),
annotations::AnnotationScope::Module, annotations::AnnotationScope::Module,
exec_state, exec_state,
) )
@ -100,7 +95,7 @@ impl ExecutorContext {
let mut last_expr = None; let mut last_expr = None;
// Iterate over the body of the program. // Iterate over the body of the program.
for (i, statement) in program.body.iter().enumerate() { for statement in &program.body {
match statement { match statement {
BodyItem::ImportStatement(import_stmt) => { BodyItem::ImportStatement(import_stmt) => {
if body_type != BodyType::Root { if body_type != BodyType::Root {
@ -111,9 +106,9 @@ impl ExecutorContext {
} }
let source_range = SourceRange::from(import_stmt); let source_range = SourceRange::from(import_stmt);
let meta_nodes = program.non_code_meta.get(i); let attrs = &import_stmt.outer_attrs;
let module_id = self let module_id = self
.open_module(&import_stmt.path, meta_nodes, exec_state, source_range) .open_module(&import_stmt.path, attrs, exec_state, source_range)
.await?; .await?;
match &import_stmt.selector { match &import_stmt.selector {
@ -209,7 +204,7 @@ impl ExecutorContext {
let source_range = SourceRange::from(&variable_declaration.declaration.init); let source_range = SourceRange::from(&variable_declaration.declaration.init);
let metadata = Metadata { source_range }; let metadata = Metadata { source_range };
let _meta_nodes = program.non_code_meta.get(i); let _annotations = &variable_declaration.outer_attrs;
let memory_item = self let memory_item = self
.execute_expr( .execute_expr(
@ -279,7 +274,7 @@ impl ExecutorContext {
async fn open_module( async fn open_module(
&self, &self,
path: &ImportPath, path: &ImportPath,
non_code_meta: &[Node<NonCodeNode>], attrs: &[Node<Annotation>],
exec_state: &mut ExecState, exec_state: &mut ExecState,
source_range: SourceRange, source_range: SourceRange,
) -> Result<ModuleId, KclError> { ) -> Result<ModuleId, KclError> {
@ -307,7 +302,7 @@ impl ExecutorContext {
let id = exec_state.next_module_id(); let id = exec_state.next_module_id();
let path = resolved_path.expect_path(); let path = resolved_path.expect_path();
let format = super::import::format_from_annotations(non_code_meta, path, source_range)?; let format = super::import::format_from_annotations(attrs, path, source_range)?;
let geom = super::import::import_foreign(path, format, exec_state, self, source_range).await?; let geom = super::import::import_foreign(path, format, exec_state, self, source_range).await?;
exec_state.add_module(id, resolved_path, ModuleRepr::Foreign(geom)); exec_state.add_module(id, resolved_path, ModuleRepr::Foreign(geom));
Ok(id) Ok(id)
@ -1838,18 +1833,7 @@ mod test {
// Run each test. // Run each test.
let func_expr = &Node::no_src(FunctionExpression { let func_expr = &Node::no_src(FunctionExpression {
params, params,
body: Node { body: crate::parsing::ast::types::Program::empty(),
inner: crate::parsing::ast::types::Program {
body: Vec::new(),
non_code_meta: Default::default(),
shebang: None,
digest: None,
},
start: 0,
end: 0,
module_id: ModuleId::default(),
trivia: Vec::new(),
},
return_type: None, return_type: None,
digest: None, digest: None,
}); });

View File

@ -19,7 +19,7 @@ use crate::{
errors::{KclError, KclErrorDetails}, errors::{KclError, KclErrorDetails},
execution::{annotations, kcl_value::UnitLen, ExecState, ExecutorContext, ImportedGeometry}, execution::{annotations, kcl_value::UnitLen, ExecState, ExecutorContext, ImportedGeometry},
fs::FileSystem, fs::FileSystem,
parsing::ast::types::{Node, NonCodeNode}, parsing::ast::types::{Annotation, Node},
source_range::SourceRange, source_range::SourceRange,
}; };
@ -155,16 +155,18 @@ pub async fn import_foreign(
} }
pub(super) fn format_from_annotations( pub(super) fn format_from_annotations(
non_code_meta: &[Node<NonCodeNode>], annotations: &[Node<Annotation>],
path: &Path, path: &Path,
import_source_range: SourceRange, import_source_range: SourceRange,
) -> Result<Option<InputFormat>, KclError> { ) -> Result<Option<InputFormat>, KclError> {
let Some(props) = annotations::unnamed_properties(non_code_meta.iter().map(|n| &n.value)) else { if annotations.is_empty() {
return Ok(None); return Ok(None);
}; }
let props = annotations.iter().flat_map(|a| a.properties.as_deref().unwrap_or(&[]));
let mut result = None; let mut result = None;
for p in props { for p in props.clone() {
if p.key.name == annotations::IMPORT_FORMAT { if p.key.name == annotations::IMPORT_FORMAT {
result = Some( result = Some(
get_import_format_from_extension(annotations::expect_ident(&p.value)?).map_err(|_| { get_import_format_from_extension(annotations::expect_ident(&p.value)?).map_err(|_| {
@ -422,8 +424,8 @@ mod test {
// no format, no options // no format, no options
let text = "@()\nimport '../foo.gltf' as foo"; let text = "@()\nimport '../foo.gltf' as foo";
let parsed = crate::Program::parse_no_errs(text).unwrap().ast; let parsed = crate::Program::parse_no_errs(text).unwrap().ast;
let non_code_meta = parsed.non_code_meta.get(0); let attrs = parsed.body[0].get_attrs();
let fmt = format_from_annotations(non_code_meta, Path::new("../foo.gltf"), SourceRange::default()) let fmt = format_from_annotations(attrs, Path::new("../foo.gltf"), SourceRange::default())
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
@ -434,8 +436,8 @@ mod test {
// format, no options // format, no options
let text = "@(format = gltf)\nimport '../foo.txt' as foo"; let text = "@(format = gltf)\nimport '../foo.txt' as foo";
let parsed = crate::Program::parse_no_errs(text).unwrap().ast; let parsed = crate::Program::parse_no_errs(text).unwrap().ast;
let non_code_meta = parsed.non_code_meta.get(0); let attrs = parsed.body[0].get_attrs();
let fmt = format_from_annotations(non_code_meta, Path::new("../foo.txt"), SourceRange::default()) let fmt = format_from_annotations(attrs, Path::new("../foo.txt"), SourceRange::default())
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
@ -444,7 +446,7 @@ mod test {
); );
// format, no extension (wouldn't parse but might some day) // format, no extension (wouldn't parse but might some day)
let fmt = format_from_annotations(non_code_meta, Path::new("../foo"), SourceRange::default()) let fmt = format_from_annotations(attrs, Path::new("../foo"), SourceRange::default())
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
@ -455,8 +457,8 @@ mod test {
// format, options // format, options
let text = "@(format = obj, coords = vulkan, lengthUnit = ft)\nimport '../foo.txt' as foo"; let text = "@(format = obj, coords = vulkan, lengthUnit = ft)\nimport '../foo.txt' as foo";
let parsed = crate::Program::parse_no_errs(text).unwrap().ast; let parsed = crate::Program::parse_no_errs(text).unwrap().ast;
let non_code_meta = parsed.non_code_meta.get(0); let attrs = parsed.body[0].get_attrs();
let fmt = format_from_annotations(non_code_meta, Path::new("../foo.txt"), SourceRange::default()) let fmt = format_from_annotations(attrs, Path::new("../foo.txt"), SourceRange::default())
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
@ -470,8 +472,8 @@ mod test {
// no format, options // no format, options
let text = "@(coords = vulkan, lengthUnit = ft)\nimport '../foo.obj' as foo"; let text = "@(coords = vulkan, lengthUnit = ft)\nimport '../foo.obj' as foo";
let parsed = crate::Program::parse_no_errs(text).unwrap().ast; let parsed = crate::Program::parse_no_errs(text).unwrap().ast;
let non_code_meta = parsed.non_code_meta.get(0); let attrs = parsed.body[0].get_attrs();
let fmt = format_from_annotations(non_code_meta, Path::new("../foo.obj"), SourceRange::default()) let fmt = format_from_annotations(attrs, Path::new("../foo.obj"), SourceRange::default())
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
@ -523,8 +525,8 @@ mod test {
#[track_caller] #[track_caller]
fn assert_annotation_error(src: &str, path: &str, expected: &str) { fn assert_annotation_error(src: &str, path: &str, expected: &str) {
let parsed = crate::Program::parse_no_errs(src).unwrap().ast; let parsed = crate::Program::parse_no_errs(src).unwrap().ast;
let non_code_meta = parsed.non_code_meta.get(0); let attrs = parsed.body[0].get_attrs();
let err = format_from_annotations(non_code_meta, Path::new(path), SourceRange::default()).unwrap_err(); let err = format_from_annotations(attrs, Path::new(path), SourceRange::default()).unwrap_err();
assert!( assert!(
err.message().contains(expected), err.message().contains(expected),
"Expected: `{expected}`, found `{}`", "Expected: `{expected}`, found `{}`",

View File

@ -12,7 +12,7 @@ use crate::{
ExecOutcome, ExecutorSettings, KclValue, Operation, UnitAngle, UnitLen, ExecOutcome, ExecutorSettings, KclValue, Operation, UnitAngle, UnitLen,
}, },
modules::{ModuleId, ModuleInfo, ModuleLoader, ModulePath, ModuleRepr}, modules::{ModuleId, ModuleInfo, ModuleLoader, ModulePath, ModuleRepr},
parsing::ast::types::NonCodeValue, parsing::ast::types::Annotation,
source_range::SourceRange, source_range::SourceRange,
}; };
@ -236,21 +236,20 @@ pub struct MetaSettings {
impl MetaSettings { impl MetaSettings {
pub(crate) fn update_from_annotation( pub(crate) fn update_from_annotation(
&mut self, &mut self,
annotation: &NonCodeValue, annotation: &crate::parsing::ast::types::Node<Annotation>,
source_range: SourceRange,
) -> Result<(), KclError> { ) -> Result<(), KclError> {
let properties = annotations::expect_properties(annotations::SETTINGS, annotation, source_range)?; let properties = annotations::expect_properties(annotations::SETTINGS, annotation)?;
for p in properties { for p in properties {
match &*p.inner.key.name { match &*p.inner.key.name {
annotations::SETTINGS_UNIT_LENGTH => { annotations::SETTINGS_UNIT_LENGTH => {
let value = annotations::expect_ident(&p.inner.value)?; let value = annotations::expect_ident(&p.inner.value)?;
let value = kcl_value::UnitLen::from_str(value, source_range)?; let value = kcl_value::UnitLen::from_str(value, annotation.as_source_range())?;
self.default_length_units = value; self.default_length_units = value;
} }
annotations::SETTINGS_UNIT_ANGLE => { annotations::SETTINGS_UNIT_ANGLE => {
let value = annotations::expect_ident(&p.inner.value)?; let value = annotations::expect_ident(&p.inner.value)?;
let value = kcl_value::UnitAngle::from_str(value, source_range)?; let value = kcl_value::UnitAngle::from_str(value, annotation.as_source_range())?;
self.default_angle_units = value; self.default_angle_units = value;
} }
name => { name => {
@ -260,7 +259,7 @@ impl MetaSettings {
annotations::SETTINGS_UNIT_LENGTH, annotations::SETTINGS_UNIT_LENGTH,
annotations::SETTINGS_UNIT_ANGLE annotations::SETTINGS_UNIT_ANGLE
), ),
source_ranges: vec![source_range], source_ranges: vec![annotation.as_source_range()],
})) }))
} }
} }

View File

@ -1,13 +1,10 @@
use sha2::{Digest as DigestTrait, Sha256}; use sha2::{Digest as DigestTrait, Sha256};
use super::types::{ use super::types::{DefaultParamVal, ItemVisibility, LabelledExpression, LiteralValue, VariableKind};
DefaultParamVal, ItemVisibility, LabelledExpression, LiteralValue, NonCodeMeta, NonCodeNode, NonCodeValue,
VariableKind,
};
use crate::parsing::ast::types::{ use crate::parsing::ast::types::{
ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryPart, BodyItem, CallExpression, CallExpressionKw, Annotation, ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryPart, BodyItem, CallExpression,
ElseIf, Expr, ExpressionStatement, FnArgType, FunctionExpression, Identifier, IfExpression, ImportItem, CallExpressionKw, ElseIf, Expr, ExpressionStatement, FnArgType, FunctionExpression, Identifier, IfExpression,
ImportSelector, ImportStatement, KclNone, Literal, LiteralIdentifier, MemberExpression, MemberObject, ImportItem, ImportSelector, ImportStatement, KclNone, Literal, LiteralIdentifier, MemberExpression, MemberObject,
ObjectExpression, ObjectProperty, Parameter, PipeExpression, PipeSubstitution, Program, ReturnStatement, ObjectExpression, ObjectProperty, Parameter, PipeExpression, PipeSubstitution, Program, ReturnStatement,
TagDeclarator, UnaryExpression, VariableDeclaration, VariableDeclarator, TagDeclarator, UnaryExpression, VariableDeclaration, VariableDeclarator,
}; };
@ -82,49 +79,22 @@ impl Program {
for body_item in slf.body.iter_mut() { for body_item in slf.body.iter_mut() {
hasher.update(body_item.compute_digest()); hasher.update(body_item.compute_digest());
} }
// This contains settings annotations. for attr in &mut slf.inner_attrs {
hasher.update(slf.non_code_meta.compute_digest()); hasher.update(attr.compute_digest());
}
if let Some(shebang) = &slf.shebang { if let Some(shebang) = &slf.shebang {
hasher.update(&shebang.inner.content); hasher.update(&shebang.inner.content);
} }
}); });
} }
impl NonCodeMeta { impl Annotation {
compute_digest!(|slf, hasher| {
for non_code_node in slf.start_nodes.iter_mut() {
hasher.update(non_code_node.compute_digest());
}
for (_, non_code_nodes) in slf.non_code_nodes.iter_mut() {
for non_code_node in non_code_nodes.iter_mut() {
hasher.update(non_code_node.compute_digest());
}
}
});
}
impl NonCodeNode {
compute_digest!(|slf, hasher| {
hasher.update(slf.value.compute_digest());
});
}
impl NonCodeValue {
pub fn compute_digest(&mut self) -> Digest { pub fn compute_digest(&mut self) -> Digest {
let mut hasher = Sha256::new(); let mut hasher = Sha256::new();
match self { if let Some(name) = &mut self.name {
NonCodeValue::InlineComment { .. } => {}
NonCodeValue::BlockComment { .. } => {}
NonCodeValue::NewLineBlockComment { .. } => {}
NonCodeValue::NewLine => {}
NonCodeValue::Annotation {
ref mut name,
properties,
} => {
if let Some(name) = name {
hasher.update(name.compute_digest()); hasher.update(name.compute_digest());
} }
if let Some(properties) = properties { if let Some(properties) = &mut self.properties {
hasher.update(properties.len().to_ne_bytes()); hasher.update(properties.len().to_ne_bytes());
for property in properties.iter_mut() { for property in properties.iter_mut() {
hasher.update(property.compute_digest()); hasher.update(property.compute_digest());
@ -132,20 +102,24 @@ impl NonCodeValue {
} else { } else {
hasher.update("no_properties"); hasher.update("no_properties");
} }
}
}
hasher.finalize().into() hasher.finalize().into()
} }
} }
impl BodyItem { impl BodyItem {
pub fn compute_digest(&mut self) -> Digest { pub fn compute_digest(&mut self) -> Digest {
match self { let mut hasher = Sha256::new();
hasher.update(match self {
BodyItem::ImportStatement(s) => s.compute_digest(), BodyItem::ImportStatement(s) => s.compute_digest(),
BodyItem::ExpressionStatement(es) => es.compute_digest(), BodyItem::ExpressionStatement(es) => es.compute_digest(),
BodyItem::VariableDeclaration(vs) => vs.compute_digest(), BodyItem::VariableDeclaration(vs) => vs.compute_digest(),
BodyItem::ReturnStatement(rs) => rs.compute_digest(), BodyItem::ReturnStatement(rs) => rs.compute_digest(),
});
for a in self.get_attrs_mut() {
hasher.update(a.compute_digest());
} }
hasher.finalize().into()
} }
} }

View File

@ -52,7 +52,7 @@ pub struct Node<T> {
#[serde(default, skip_serializing_if = "ModuleId::is_top_level")] #[serde(default, skip_serializing_if = "ModuleId::is_top_level")]
pub module_id: ModuleId, pub module_id: ModuleId,
#[serde(default, skip_serializing_if = "Vec::is_empty")] #[serde(default, skip_serializing_if = "Vec::is_empty")]
pub trivia: NodeList<NonCodeNode>, pub outer_attrs: NodeList<Annotation>,
} }
impl<T> Node<T> { impl<T> Node<T> {
@ -96,7 +96,7 @@ impl<T> Node<T> {
start, start,
end, end,
module_id, module_id,
trivia: Vec::new(), outer_attrs: Vec::new(),
} }
} }
@ -106,7 +106,7 @@ impl<T> Node<T> {
start: 0, start: 0,
end: 0, end: 0,
module_id: ModuleId::default(), module_id: ModuleId::default(),
trivia: Vec::new(), outer_attrs: Vec::new(),
} }
} }
@ -116,7 +116,7 @@ impl<T> Node<T> {
start, start,
end, end,
module_id, module_id,
trivia: Vec::new(), outer_attrs: Vec::new(),
}) })
} }
@ -181,6 +181,8 @@ pub struct Program {
pub non_code_meta: NonCodeMeta, pub non_code_meta: NonCodeMeta,
#[serde(default, skip_serializing_if = "Option::is_none")] #[serde(default, skip_serializing_if = "Option::is_none")]
pub shebang: Option<Node<Shebang>>, pub shebang: Option<Node<Shebang>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub inner_attrs: NodeList<Annotation>,
#[serde(default, skip_serializing_if = "Option::is_none")] #[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional)] #[ts(optional)]
@ -261,28 +263,12 @@ impl Node<Program> {
Ok(findings) Ok(findings)
} }
pub fn annotations(&self) -> impl Iterator<Item = &Node<NonCodeNode>> {
self.non_code_meta
.start_nodes
.iter()
.filter(|n| n.value_is_annotation())
}
pub fn annotations_mut(&mut self) -> impl Iterator<Item = &mut Node<NonCodeNode>> {
self.non_code_meta
.start_nodes
.iter_mut()
.filter(|n| n.value_is_annotation())
}
/// Get the annotations for the meta settings from the kcl file. /// Get the annotations for the meta settings from the kcl file.
pub fn meta_settings(&self) -> Result<Option<crate::execution::MetaSettings>, KclError> { pub fn meta_settings(&self) -> Result<Option<crate::execution::MetaSettings>, KclError> {
for annotation_node in self.annotations() { for annotation in &self.inner_attrs {
let annotation = &annotation_node.value; if annotation.name() == Some(annotations::SETTINGS) {
if annotation.annotation_name() == Some(annotations::SETTINGS) {
let source_range = annotation_node.as_source_range();
let mut meta_settings = crate::execution::MetaSettings::default(); let mut meta_settings = crate::execution::MetaSettings::default();
meta_settings.update_from_annotation(annotation, source_range)?; meta_settings.update_from_annotation(annotation)?;
return Ok(Some(meta_settings)); return Ok(Some(meta_settings));
} }
} }
@ -293,24 +279,18 @@ impl Node<Program> {
pub fn change_meta_settings(&mut self, settings: crate::execution::MetaSettings) -> Result<Self, KclError> { pub fn change_meta_settings(&mut self, settings: crate::execution::MetaSettings) -> Result<Self, KclError> {
let mut new_program = self.clone(); let mut new_program = self.clone();
let mut found = false; let mut found = false;
for node in new_program.annotations_mut() { for node in &mut new_program.inner_attrs {
if node.value.annotation_name() == Some(annotations::SETTINGS) { if node.name() == Some(annotations::SETTINGS) {
let annotation = NonCodeValue::new_from_meta_settings(&settings); *node = Node::no_src(Annotation::new_from_meta_settings(&settings));
*node = Node::no_src(NonCodeNode {
value: annotation,
digest: None,
});
found = true; found = true;
break; break;
} }
} }
if !found { if !found {
let annotation = NonCodeValue::new_from_meta_settings(&settings); new_program
new_program.non_code_meta.start_nodes.push(Node::no_src(NonCodeNode { .inner_attrs
value: annotation, .push(Node::no_src(Annotation::new_from_meta_settings(&settings)));
digest: None,
}));
} }
Ok(new_program) Ok(new_program)
@ -318,6 +298,10 @@ impl Node<Program> {
} }
impl Program { impl Program {
#[cfg(test)]
pub fn empty() -> Node<Self> {
Node::no_src(Program::default())
}
/// Is the last body item an expression? /// Is the last body item an expression?
pub fn ends_with_expr(&self) -> bool { pub fn ends_with_expr(&self) -> bool {
let Some(ref last) = self.body.last() else { let Some(ref last) = self.body.last() else {
@ -629,6 +613,33 @@ impl BodyItem {
BodyItem::ReturnStatement(return_statement) => return_statement.end, BodyItem::ReturnStatement(return_statement) => return_statement.end,
} }
} }
pub(crate) fn set_attrs(&mut self, attr: NodeList<Annotation>) {
match self {
BodyItem::ImportStatement(node) => node.outer_attrs = attr,
BodyItem::ExpressionStatement(node) => node.outer_attrs = attr,
BodyItem::VariableDeclaration(node) => node.outer_attrs = attr,
BodyItem::ReturnStatement(node) => node.outer_attrs = attr,
}
}
pub(crate) fn get_attrs(&self) -> &[Node<Annotation>] {
match self {
BodyItem::ImportStatement(node) => &node.outer_attrs,
BodyItem::ExpressionStatement(node) => &node.outer_attrs,
BodyItem::VariableDeclaration(node) => &node.outer_attrs,
BodyItem::ReturnStatement(node) => &node.outer_attrs,
}
}
pub(crate) fn get_attrs_mut(&mut self) -> &mut [Node<Annotation>] {
match self {
BodyItem::ImportStatement(node) => &mut node.outer_attrs,
BodyItem::ExpressionStatement(node) => &mut node.outer_attrs,
BodyItem::VariableDeclaration(node) => &mut node.outer_attrs,
BodyItem::ReturnStatement(node) => &mut node.outer_attrs,
}
}
} }
impl From<BodyItem> for SourceRange { impl From<BodyItem> for SourceRange {
@ -1134,24 +1145,6 @@ impl NonCodeNode {
NonCodeValue::BlockComment { value, style: _ } => value.clone(), NonCodeValue::BlockComment { value, style: _ } => value.clone(),
NonCodeValue::NewLineBlockComment { value, style: _ } => value.clone(), NonCodeValue::NewLineBlockComment { value, style: _ } => value.clone(),
NonCodeValue::NewLine => "\n\n".to_string(), NonCodeValue::NewLine => "\n\n".to_string(),
n @ NonCodeValue::Annotation { .. } => n.annotation_name().unwrap_or("").to_owned(),
}
}
pub fn annotation(&self) -> Option<&NonCodeValue> {
match &self.value {
a @ NonCodeValue::Annotation { .. } => Some(a),
_ => None,
}
}
pub fn value_is_annotation(&self) -> bool {
match self.value {
NonCodeValue::InlineComment { .. }
| NonCodeValue::BlockComment { .. }
| NonCodeValue::NewLineBlockComment { .. }
| NonCodeValue::NewLine => false,
NonCodeValue::Annotation { .. } => true,
} }
} }
} }
@ -1202,37 +1195,6 @@ pub enum NonCodeValue {
// A new line like `\n\n` NOT a new line like `\n`. // A new line like `\n\n` NOT a new line like `\n`.
// This is also not a comment. // This is also not a comment.
NewLine, NewLine,
Annotation {
name: Option<Node<Identifier>>,
properties: Option<Vec<Node<ObjectProperty>>>,
},
}
impl NonCodeValue {
pub fn annotation_name(&self) -> Option<&str> {
match self {
NonCodeValue::Annotation { name, .. } => name.as_ref().map(|i| &*i.name),
_ => None,
}
}
pub fn new_from_meta_settings(settings: &crate::execution::MetaSettings) -> NonCodeValue {
let mut properties: Vec<Node<ObjectProperty>> = vec![ObjectProperty::new(
Identifier::new(annotations::SETTINGS_UNIT_LENGTH),
Expr::Identifier(Box::new(Identifier::new(&settings.default_length_units.to_string()))),
)];
if settings.default_angle_units != Default::default() {
properties.push(ObjectProperty::new(
Identifier::new(annotations::SETTINGS_UNIT_ANGLE),
Expr::Identifier(Box::new(Identifier::new(&settings.default_angle_units.to_string()))),
));
}
NonCodeValue::Annotation {
name: Some(Identifier::new(annotations::SETTINGS)),
properties: Some(properties),
}
}
} }
#[derive(Debug, Default, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)] #[derive(Debug, Default, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
@ -1314,6 +1276,47 @@ impl<'de> Deserialize<'de> for NonCodeMeta {
} }
} }
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)]
#[serde(tag = "type")]
pub struct Annotation {
pub name: Option<Node<Identifier>>,
pub properties: Option<Vec<Node<ObjectProperty>>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub digest: Option<Digest>,
}
impl Annotation {
pub fn is_inner(&self) -> bool {
self.name.is_some()
}
pub fn name(&self) -> Option<&str> {
self.name.as_ref().map(|n| &*n.name)
}
pub fn new_from_meta_settings(settings: &crate::execution::MetaSettings) -> Annotation {
let mut properties: Vec<Node<ObjectProperty>> = vec![ObjectProperty::new(
Identifier::new(annotations::SETTINGS_UNIT_LENGTH),
Expr::Identifier(Box::new(Identifier::new(&settings.default_length_units.to_string()))),
)];
if settings.default_angle_units != Default::default() {
properties.push(ObjectProperty::new(
Identifier::new(annotations::SETTINGS_UNIT_ANGLE),
Expr::Identifier(Box::new(Identifier::new(&settings.default_angle_units.to_string()))),
));
}
Annotation {
name: Some(Identifier::new(annotations::SETTINGS)),
properties: Some(properties),
digest: None,
}
}
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)] #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)] #[ts(export)]
#[serde(tag = "type")] #[serde(tag = "type")]
@ -3823,12 +3826,7 @@ const cylinder = startSketchOn('-XZ')
(0..=0), (0..=0),
Node::no_src(FunctionExpression { Node::no_src(FunctionExpression {
params: vec![], params: vec![],
body: Node::no_src(Program { body: Program::empty(),
body: Vec::new(),
non_code_meta: Default::default(),
shebang: None,
digest: None,
}),
return_type: None, return_type: None,
digest: None, digest: None,
}), }),
@ -3847,18 +3845,7 @@ const cylinder = startSketchOn('-XZ')
labeled: true, labeled: true,
digest: None, digest: None,
}], }],
body: Node { body: Program::empty(),
inner: Program {
body: Vec::new(),
non_code_meta: Default::default(),
shebang: None,
digest: None,
},
start: 0,
end: 0,
module_id: ModuleId::default(),
trivia: Vec::new(),
},
return_type: None, return_type: None,
digest: None, digest: None,
}), }),
@ -3877,18 +3864,7 @@ const cylinder = startSketchOn('-XZ')
labeled: true, labeled: true,
digest: None, digest: None,
}], }],
body: Node { body: Program::empty(),
inner: Program {
body: Vec::new(),
non_code_meta: Default::default(),
shebang: None,
digest: None,
},
start: 0,
end: 0,
module_id: ModuleId::default(),
trivia: Vec::new(),
},
return_type: None, return_type: None,
digest: None, digest: None,
}), }),
@ -3919,18 +3895,7 @@ const cylinder = startSketchOn('-XZ')
digest: None, digest: None,
}, },
], ],
body: Node { body: Program::empty(),
inner: Program {
body: Vec::new(),
non_code_meta: Default::default(),
shebang: None,
digest: None,
},
start: 0,
end: 0,
module_id: ModuleId::default(),
trivia: Vec::new(),
},
return_type: None, return_type: None,
digest: None, digest: None,
}), }),

View File

@ -21,13 +21,13 @@ use crate::{
errors::{CompilationError, Severity, Tag}, errors::{CompilationError, Severity, Tag},
parsing::{ parsing::{
ast::types::{ ast::types::{
ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem, BoxNode, Annotation, ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem,
CallExpression, CallExpressionKw, CommentStyle, DefaultParamVal, ElseIf, Expr, ExpressionStatement, BoxNode, CallExpression, CallExpressionKw, CommentStyle, DefaultParamVal, ElseIf, Expr,
FnArgPrimitive, FnArgType, FunctionExpression, Identifier, IfExpression, ImportItem, ImportSelector, ExpressionStatement, FnArgPrimitive, FnArgType, FunctionExpression, Identifier, IfExpression, ImportItem,
ImportStatement, ItemVisibility, LabeledArg, Literal, LiteralIdentifier, LiteralValue, MemberExpression, ImportSelector, ImportStatement, ItemVisibility, LabeledArg, Literal, LiteralIdentifier, LiteralValue,
MemberObject, Node, NodeList, NonCodeMeta, NonCodeNode, NonCodeValue, ObjectExpression, ObjectProperty, MemberExpression, MemberObject, Node, NodeList, NonCodeMeta, NonCodeNode, NonCodeValue, ObjectExpression,
Parameter, PipeExpression, PipeSubstitution, Program, ReturnStatement, Shebang, TagDeclarator, ObjectProperty, Parameter, PipeExpression, PipeSubstitution, Program, ReturnStatement, Shebang,
UnaryExpression, UnaryOperator, VariableDeclaration, VariableDeclarator, VariableKind, TagDeclarator, UnaryExpression, UnaryOperator, VariableDeclaration, VariableDeclarator, VariableKind,
}, },
math::BinaryExpressionToken, math::BinaryExpressionToken,
token::{Token, TokenSlice, TokenType}, token::{Token, TokenSlice, TokenType},
@ -284,7 +284,7 @@ fn non_code_node(i: &mut TokenSlice) -> PResult<Node<NonCodeNode>> {
alt((non_code_node_leading_whitespace, non_code_node_no_leading_whitespace)).parse_next(i) alt((non_code_node_leading_whitespace, non_code_node_no_leading_whitespace)).parse_next(i)
} }
fn annotation(i: &mut TokenSlice) -> PResult<Node<NonCodeNode>> { fn annotation(i: &mut TokenSlice) -> PResult<Node<Annotation>> {
let at = at_sign.parse_next(i)?; let at = at_sign.parse_next(i)?;
let name = opt(binding_name).parse_next(i)?; let name = opt(binding_name).parse_next(i)?;
let mut end = name.as_ref().map(|n| n.end).unwrap_or(at.end); let mut end = name.as_ref().map(|n| n.end).unwrap_or(at.end);
@ -308,7 +308,7 @@ fn annotation(i: &mut TokenSlice) -> PResult<Node<NonCodeNode>> {
value, value,
digest: None, digest: None,
}, },
trivia: Vec::new(), outer_attrs: Vec::new(),
}), }),
comma_sep, comma_sep,
) )
@ -327,19 +327,16 @@ fn annotation(i: &mut TokenSlice) -> PResult<Node<NonCodeNode>> {
)); ));
} }
let value = NonCodeValue::Annotation { name, properties }; let value = Annotation {
Ok(Node::new( name,
NonCodeNode { value, digest: None }, properties,
at.start, digest: None,
end, };
at.module_id, Ok(Node::new(value, at.start, end, at.module_id))
))
} }
// Matches remaining three cases of NonCodeValue // Matches remaining three cases of NonCodeValue
fn non_code_node_no_leading_whitespace(i: &mut TokenSlice) -> PResult<Node<NonCodeNode>> { fn non_code_node_no_leading_whitespace(i: &mut TokenSlice) -> PResult<Node<NonCodeNode>> {
alt((
annotation,
any.verify_map(|token: Token| { any.verify_map(|token: Token| {
if token.is_code_token() { if token.is_code_token() {
None None
@ -369,8 +366,7 @@ fn non_code_node_no_leading_whitespace(i: &mut TokenSlice) -> PResult<Node<NonCo
)) ))
} }
}) })
.context(expected("Non-code token (comments or whitespace)")), .context(expected("Non-code token (comments or whitespace)"))
))
.parse_next(i) .parse_next(i)
} }
@ -427,7 +423,7 @@ fn pipe_expression(i: &mut TokenSlice) -> PResult<Node<PipeExpression>> {
non_code_meta, non_code_meta,
digest: None, digest: None,
}, },
trivia: Vec::new(), outer_attrs: Vec::new(),
}) })
} }
@ -830,7 +826,7 @@ fn object_property_same_key_and_val(i: &mut TokenSlice) -> PResult<Node<ObjectPr
key, key,
digest: None, digest: None,
}, },
trivia: Vec::new(), outer_attrs: Vec::new(),
}) })
} }
@ -859,7 +855,7 @@ fn object_property(i: &mut TokenSlice) -> PResult<Node<ObjectProperty>> {
value: expr, value: expr,
digest: None, digest: None,
}, },
trivia: Vec::new(), outer_attrs: Vec::new(),
}; };
if sep.token_type == TokenType::Colon { if sep.token_type == TokenType::Colon {
@ -1131,17 +1127,7 @@ fn function_decl(i: &mut TokenSlice) -> PResult<(Node<FunctionExpression>, bool)
let close: Option<(Vec<Vec<Token>>, Token)> = opt((repeat(0.., whitespace), close_brace)).parse_next(i)?; let close: Option<(Vec<Vec<Token>>, Token)> = opt((repeat(0.., whitespace), close_brace)).parse_next(i)?;
let (body, end) = match close { let (body, end) = match close {
Some((_, end)) => ( Some((_, end)) => (
Node::new( Node::new(Program::default(), brace.end, brace.end, brace.module_id),
Program {
body: Vec::new(),
non_code_meta: NonCodeMeta::default(),
shebang: None,
digest: None,
},
brace.end,
brace.end,
brace.module_id,
),
end.end, end.end,
), ),
None => (function_body(i)?, close_brace(i)?.end), None => (function_body(i)?, close_brace(i)?.end),
@ -1277,7 +1263,6 @@ fn noncode_just_after_code(i: &mut TokenSlice) -> PResult<Node<NonCodeNode>> {
x @ NonCodeValue::InlineComment { .. } => x, x @ NonCodeValue::InlineComment { .. } => x,
x @ NonCodeValue::NewLineBlockComment { .. } => x, x @ NonCodeValue::NewLineBlockComment { .. } => x,
x @ NonCodeValue::NewLine => x, x @ NonCodeValue::NewLine => x,
x @ NonCodeValue::Annotation { .. } => x,
}; };
Node::new( Node::new(
NonCodeNode { value, ..nc.inner }, NonCodeNode { value, ..nc.inner },
@ -1298,7 +1283,6 @@ fn noncode_just_after_code(i: &mut TokenSlice) -> PResult<Node<NonCodeNode>> {
x @ NonCodeValue::InlineComment { .. } => x, x @ NonCodeValue::InlineComment { .. } => x,
x @ NonCodeValue::NewLineBlockComment { .. } => x, x @ NonCodeValue::NewLineBlockComment { .. } => x,
x @ NonCodeValue::NewLine => x, x @ NonCodeValue::NewLine => x,
x @ NonCodeValue::Annotation { .. } => x,
}; };
Node::new(NonCodeNode { value, ..nc.inner }, nc.start, nc.end, nc.module_id) Node::new(NonCodeNode { value, ..nc.inner }, nc.start, nc.end, nc.module_id)
} }
@ -1314,6 +1298,7 @@ fn noncode_just_after_code(i: &mut TokenSlice) -> PResult<Node<NonCodeNode>> {
#[derive(Debug)] #[derive(Debug)]
#[allow(clippy::large_enum_variant)] #[allow(clippy::large_enum_variant)]
enum WithinFunction { enum WithinFunction {
Annotation(Node<Annotation>),
BodyItem((BodyItem, Option<Node<NonCodeNode>>)), BodyItem((BodyItem, Option<Node<NonCodeNode>>)),
NonCode(Node<NonCodeNode>), NonCode(Node<NonCodeNode>),
} }
@ -1338,9 +1323,12 @@ fn body_items_within_function(i: &mut TokenSlice) -> PResult<WithinFunction> {
(import_stmt.map(BodyItem::ImportStatement), opt(noncode_just_after_code)).map(WithinFunction::BodyItem), (import_stmt.map(BodyItem::ImportStatement), opt(noncode_just_after_code)).map(WithinFunction::BodyItem),
Token { ref value, .. } if value == "return" => Token { ref value, .. } if value == "return" =>
(return_stmt.map(BodyItem::ReturnStatement), opt(noncode_just_after_code)).map(WithinFunction::BodyItem), (return_stmt.map(BodyItem::ReturnStatement), opt(noncode_just_after_code)).map(WithinFunction::BodyItem),
token if !token.is_code_token() || token.token_type == TokenType::At => { token if !token.is_code_token() => {
non_code_node.map(WithinFunction::NonCode) non_code_node.map(WithinFunction::NonCode)
}, },
token if token.token_type == TokenType::At => {
annotation.map(WithinFunction::Annotation)
},
_ => _ =>
alt(( alt((
( (
@ -1447,16 +1435,32 @@ fn function_body(i: &mut TokenSlice) -> PResult<Node<Program>> {
} }
let mut body = Vec::new(); let mut body = Vec::new();
let mut inner_attrs = Vec::new();
let mut pending_attrs = Vec::new();
let mut non_code_meta = NonCodeMeta::default(); let mut non_code_meta = NonCodeMeta::default();
let mut end = 0; let mut end = 0;
let mut start = leading_whitespace_start; let mut start = leading_whitespace_start;
for thing_in_body in things_within_body { for thing_in_body in things_within_body {
match thing_in_body { match thing_in_body {
WithinFunction::BodyItem((b, maybe_noncode)) => { WithinFunction::Annotation(attr) => {
if start.is_none() {
start = Some((attr.start, attr.module_id))
}
if attr.is_inner() {
inner_attrs.push(attr);
} else {
pending_attrs.push(attr);
}
}
WithinFunction::BodyItem((mut b, maybe_noncode)) => {
if start.is_none() { if start.is_none() {
start = Some((b.start(), b.module_id())); start = Some((b.start(), b.module_id()));
} }
end = b.end(); end = b.end();
if !pending_attrs.is_empty() {
b.set_attrs(pending_attrs);
pending_attrs = Vec::new();
}
body.push(b); body.push(b);
if let Some(nc) = maybe_noncode { if let Some(nc) = maybe_noncode {
end = nc.end; end = nc.end;
@ -1476,9 +1480,26 @@ fn function_body(i: &mut TokenSlice) -> PResult<Node<Program>> {
} }
} }
} }
let start = start.expect( let start = start.expect(
"the `things_within_body` vec should have looped at least once, and each loop overwrites `start` if it is None", "the `things_within_body` vec should have looped at least once, and each loop overwrites `start` if it is None",
); );
if !pending_attrs.is_empty() {
for a in pending_attrs {
ParseContext::err(CompilationError::err(
a.as_source_range(),
"Attribute is not attached to any item",
));
}
return Err(ErrMode::Cut(
CompilationError::fatal(
SourceRange::new(start.0, end, start.1),
"Block contains un-attached attributes",
)
.into(),
));
}
// Safe to unwrap `body.first()` because `body` is `separated1` therefore guaranteed // Safe to unwrap `body.first()` because `body` is `separated1` therefore guaranteed
// to have len >= 1. // to have len >= 1.
let end_ws = opt(whitespace) let end_ws = opt(whitespace)
@ -1492,6 +1513,7 @@ fn function_body(i: &mut TokenSlice) -> PResult<Node<Program>> {
Program { Program {
body, body,
non_code_meta, non_code_meta,
inner_attrs,
shebang: None, shebang: None,
digest: None, digest: None,
}, },
@ -1790,7 +1812,7 @@ fn return_stmt(i: &mut TokenSlice) -> PResult<Node<ReturnStatement>> {
end: argument.end(), end: argument.end(),
module_id: ret.module_id, module_id: ret.module_id,
inner: ReturnStatement { argument, digest: None }, inner: ReturnStatement { argument, digest: None },
trivia: Vec::new(), outer_attrs: Vec::new(),
}) })
} }
@ -2017,13 +2039,13 @@ fn declaration(i: &mut TokenSlice) -> PResult<BoxNode<VariableDeclaration>> {
init: val, init: val,
digest: None, digest: None,
}, },
trivia: Vec::new(), outer_attrs: Vec::new(),
}, },
visibility, visibility,
kind, kind,
digest: None, digest: None,
}, },
trivia: Vec::new(), outer_attrs: Vec::new(),
})) }))
} }
@ -2229,7 +2251,7 @@ fn unary_expression(i: &mut TokenSlice) -> PResult<Node<UnaryExpression>> {
argument, argument,
digest: None, digest: None,
}, },
trivia: Vec::new(), outer_attrs: Vec::new(),
}) })
} }
@ -2310,7 +2332,7 @@ fn expression_stmt(i: &mut TokenSlice) -> PResult<Node<ExpressionStatement>> {
expression: val, expression: val,
digest: None, digest: None,
}, },
trivia: Vec::new(), outer_attrs: Vec::new(),
}) })
} }
@ -2750,7 +2772,7 @@ fn fn_call(i: &mut TokenSlice) -> PResult<Node<CallExpression>> {
arguments: args, arguments: args,
digest: None, digest: None,
}, },
trivia: Vec::new(), outer_attrs: Vec::new(),
}) })
} }
@ -2780,7 +2802,7 @@ fn fn_call_kw(i: &mut TokenSlice) -> PResult<Node<CallExpressionKw>> {
arguments: args, arguments: args,
digest: None, digest: None,
}, },
trivia: Vec::new(), outer_attrs: Vec::new(),
}) })
} }
@ -3023,6 +3045,7 @@ mySk1 = startSketchAt([0, 0])"#;
)], )],
digest: None, digest: None,
}, },
inner_attrs: Vec::new(),
shebang: None, shebang: None,
digest: None, digest: None,
}, },
@ -3700,6 +3723,7 @@ mySk1 = startSketchAt([0, 0])"#;
))], ))],
shebang: None, shebang: None,
non_code_meta: NonCodeMeta::default(), non_code_meta: NonCodeMeta::default(),
inner_attrs: Vec::new(),
digest: None, digest: None,
}, },
0, 0,

View File

@ -2,11 +2,12 @@ use std::fmt::Write;
use crate::parsing::{ use crate::parsing::{
ast::types::{ ast::types::{
ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem, CallExpression, Annotation, ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem,
CallExpressionKw, CommentStyle, DefaultParamVal, Expr, FnArgType, FormatOptions, FunctionExpression, CallExpression, CallExpressionKw, CommentStyle, DefaultParamVal, Expr, FnArgType, FormatOptions,
IfExpression, ImportSelector, ImportStatement, ItemVisibility, LabeledArg, Literal, LiteralIdentifier, FunctionExpression, IfExpression, ImportSelector, ImportStatement, ItemVisibility, LabeledArg, Literal,
LiteralValue, MemberExpression, MemberObject, Node, NonCodeNode, NonCodeValue, ObjectExpression, Parameter, LiteralIdentifier, LiteralValue, MemberExpression, MemberObject, Node, NonCodeNode, NonCodeValue,
PipeExpression, Program, TagDeclarator, UnaryExpression, VariableDeclaration, VariableKind, ObjectExpression, Parameter, PipeExpression, Program, TagDeclarator, UnaryExpression, VariableDeclaration,
VariableKind,
}, },
token::NumericSuffix, token::NumericSuffix,
PIPE_OPERATOR, PIPE_OPERATOR,
@ -22,6 +23,9 @@ impl Program {
.map(|sh| format!("{}\n\n", sh.inner.content)) .map(|sh| format!("{}\n\n", sh.inner.content))
.unwrap_or_default(); .unwrap_or_default();
for attr in &self.inner_attrs {
result.push_str(&attr.recast(options, indentation_level));
}
for start in &self.non_code_meta.start_nodes { for start in &self.non_code_meta.start_nodes {
result.push_str(&start.recast(options, indentation_level)); result.push_str(&start.recast(options, indentation_level));
} }
@ -30,7 +34,12 @@ impl Program {
let result = self let result = self
.body .body
.iter() .iter()
.map(|body_item| match body_item.clone() { .map(|body_item| {
let mut result = String::new();
for attr in body_item.get_attrs() {
result.push_str(&attr.recast(options, indentation_level));
}
result.push_str(&match body_item.clone() {
BodyItem::ImportStatement(stmt) => stmt.recast(options, indentation_level), BodyItem::ImportStatement(stmt) => stmt.recast(options, indentation_level),
BodyItem::ExpressionStatement(expression_statement) => { BodyItem::ExpressionStatement(expression_statement) => {
expression_statement expression_statement
@ -50,10 +59,13 @@ impl Program {
.trim_start() .trim_start()
) )
} }
});
result
}) })
.enumerate() .enumerate()
.fold(result, |mut output, (index, recast_str)| { .fold(result, |mut output, (index, recast_str)| {
let start_string = if index == 0 && self.non_code_meta.start_nodes.is_empty() { let start_string =
if index == 0 && self.non_code_meta.start_nodes.is_empty() && self.inner_attrs.is_empty() {
// We need to indent. // We need to indent.
indentation.to_string() indentation.to_string()
} else { } else {
@ -113,9 +125,7 @@ impl NonCodeValue {
fn should_cause_array_newline(&self) -> bool { fn should_cause_array_newline(&self) -> bool {
match self { match self {
Self::InlineComment { .. } => false, Self::InlineComment { .. } => false,
Self::BlockComment { .. } | Self::NewLineBlockComment { .. } | Self::NewLine | Self::Annotation { .. } => { Self::BlockComment { .. } | Self::NewLineBlockComment { .. } | Self::NewLine => true,
true
}
} }
} }
} }
@ -156,12 +166,17 @@ impl Node<NonCodeNode> {
} }
} }
NonCodeValue::NewLine => "\n\n".to_string(), NonCodeValue::NewLine => "\n\n".to_string(),
NonCodeValue::Annotation { name, properties } => { }
}
}
impl Node<Annotation> {
fn recast(&self, options: &FormatOptions, indentation_level: usize) -> String {
let mut result = "@".to_owned(); let mut result = "@".to_owned();
if let Some(name) = name { if let Some(name) = &self.name {
result.push_str(&name.name); result.push_str(&name.name);
} }
if let Some(properties) = properties { if let Some(properties) = &self.properties {
result.push('('); result.push('(');
result.push_str( result.push_str(
&properties &properties
@ -184,8 +199,6 @@ impl Node<NonCodeNode> {
result result
} }
}
}
} }
impl ImportStatement { impl ImportStatement {

View File

@ -103,26 +103,9 @@ description: Result of parsing import_cycle1.kcl
} }
], ],
"end": 110, "end": 110,
"nonCodeMeta": { "innerAttrs": [
"nonCodeNodes": {
"0": [
{
"end": 71,
"start": 69,
"type": "NonCodeNode",
"value": {
"type": "newLine"
}
}
]
},
"startNodes": [
{ {
"end": 33, "end": 33,
"start": 0,
"type": "NonCodeNode",
"value": {
"type": "annotation",
"name": { "name": {
"end": 9, "end": 9,
"name": "settings", "name": "settings",
@ -148,11 +131,26 @@ description: Result of parsing import_cycle1.kcl
"type": "Identifier" "type": "Identifier"
} }
} }
] ],
"start": 0,
"type": "Annotation"
}
],
"nonCodeMeta": {
"nonCodeNodes": {
"0": [
{
"end": 71,
"start": 69,
"type": "NonCodeNode",
"value": {
"type": "newLine"
} }
} }
] ]
}, },
"startNodes": []
},
"start": 0 "start": 0
} }
} }

View File

@ -103,26 +103,9 @@ description: Result of parsing import_function_not_sketch.kcl
} }
], ],
"end": 109, "end": 109,
"nonCodeMeta": { "innerAttrs": [
"nonCodeNodes": {
"0": [
{
"end": 70,
"start": 68,
"type": "NonCodeNode",
"value": {
"type": "newLine"
}
}
]
},
"startNodes": [
{ {
"end": 33, "end": 33,
"start": 0,
"type": "NonCodeNode",
"value": {
"type": "annotation",
"name": { "name": {
"end": 9, "end": 9,
"name": "settings", "name": "settings",
@ -148,11 +131,26 @@ description: Result of parsing import_function_not_sketch.kcl
"type": "Identifier" "type": "Identifier"
} }
} }
] ],
"start": 0,
"type": "Annotation"
}
],
"nonCodeMeta": {
"nonCodeNodes": {
"0": [
{
"end": 70,
"start": 68,
"type": "NonCodeNode",
"value": {
"type": "newLine"
} }
} }
] ]
}, },
"startNodes": []
},
"start": 0 "start": 0
} }
} }

View File

@ -115,26 +115,9 @@ description: Result of parsing import_whole.kcl
} }
], ],
"end": 124, "end": 124,
"nonCodeMeta": { "innerAttrs": [
"nonCodeNodes": {
"0": [
{
"end": 68,
"start": 66,
"type": "NonCodeNode",
"value": {
"type": "newLine"
}
}
]
},
"startNodes": [
{ {
"end": 33, "end": 33,
"start": 0,
"type": "NonCodeNode",
"value": {
"type": "annotation",
"name": { "name": {
"end": 9, "end": 9,
"name": "settings", "name": "settings",
@ -160,11 +143,26 @@ description: Result of parsing import_whole.kcl
"type": "Identifier" "type": "Identifier"
} }
} }
] ],
"start": 0,
"type": "Annotation"
}
],
"nonCodeMeta": {
"nonCodeNodes": {
"0": [
{
"end": 68,
"start": 66,
"type": "NonCodeNode",
"value": {
"type": "newLine"
} }
} }
] ]
}, },
"startNodes": []
},
"start": 0 "start": 0
} }
} }