Move turns to a submodule of std (#6039)

* Move turns to a submodule of std

Signed-off-by: Nick Cameron <nrc@ncameron.org>

* Cache module infos as well as memory; fix a bug with deprecated constants

Signed-off-by: Nick Cameron <nrc@ncameron.org>

---------

Signed-off-by: Nick Cameron <nrc@ncameron.org>
This commit is contained in:
Nick Cameron
2025-03-30 11:10:44 +13:00
committed by GitHub
parent 51c16d0048
commit db5ce7ba85
56 changed files with 2678 additions and 2637 deletions

View File

@ -339,9 +339,9 @@ fn generate_index(combined: &IndexMap<String, Box<dyn StdLibFn>>, kcl_lib: &[Doc
}
functions.entry(d.mod_name()).or_default().push(match d {
DocData::Fn(f) => (f.name.clone(), d.file_name()),
DocData::Const(c) => (c.name.clone(), d.file_name()),
DocData::Ty(t) => (t.name.clone(), d.file_name()),
DocData::Fn(f) => (f.preferred_name.clone(), d.file_name()),
DocData::Const(c) => (c.preferred_name.clone(), d.file_name()),
DocData::Ty(t) => (t.preferred_name.clone(), d.file_name()),
});
if let DocData::Const(c) = d {

View File

@ -9,7 +9,7 @@ use tower_lsp::lsp_types::{
use crate::{
execution::annotations,
parsing::{
ast::types::{Annotation, Node, PrimitiveType, Type, VariableKind},
ast::types::{Annotation, ImportSelector, Node, PrimitiveType, Type, VariableKind},
token::NumericSuffix,
},
ModuleId,
@ -17,7 +17,7 @@ use crate::{
pub fn walk_prelude() -> Vec<DocData> {
let mut visitor = CollectionVisitor::default();
visitor.visit_module("prelude").unwrap();
visitor.visit_module("prelude", "").unwrap();
visitor.result
}
@ -29,7 +29,7 @@ struct CollectionVisitor {
}
impl CollectionVisitor {
fn visit_module(&mut self, name: &str) -> Result<(), String> {
fn visit_module(&mut self, name: &str, preferred_prefix: &str) -> Result<(), String> {
let old_name = std::mem::replace(&mut self.name, name.to_owned());
let source = crate::modules::read_std(name).unwrap();
let parsed = crate::parsing::parse_str(source, ModuleId::from_usize(self.id))
@ -40,14 +40,16 @@ impl CollectionVisitor {
for n in &parsed.body {
match n {
crate::parsing::ast::types::BodyItem::ImportStatement(import) if !import.visibility.is_default() => {
// Only supports glob imports for now.
assert!(matches!(
import.selector,
crate::parsing::ast::types::ImportSelector::Glob(..)
));
match &import.path {
crate::parsing::ast::types::ImportPath::Std { path } => {
self.visit_module(&path[1])?;
match import.selector {
ImportSelector::Glob(..) => self.visit_module(&path[1], "")?,
ImportSelector::None { .. } => {
self.visit_module(&path[1], &format!("{}::", import.module_name().unwrap()))?
}
// Only supports glob or whole-module imports for now.
_ => unimplemented!(),
}
}
p => return Err(format!("Unexpected import: `{p}`")),
}
@ -59,8 +61,8 @@ impl CollectionVisitor {
format!("std::{}::", self.name)
};
let mut dd = match var.kind {
VariableKind::Fn => DocData::Fn(FnData::from_ast(var, qual_name)),
VariableKind::Const => DocData::Const(ConstData::from_ast(var, qual_name)),
VariableKind::Fn => DocData::Fn(FnData::from_ast(var, qual_name, preferred_prefix)),
VariableKind::Const => DocData::Const(ConstData::from_ast(var, qual_name, preferred_prefix)),
};
dd.with_meta(&var.outer_attrs);
@ -77,7 +79,7 @@ impl CollectionVisitor {
} else {
format!("std::{}::", self.name)
};
let mut dd = DocData::Ty(TyData::from_ast(ty, qual_name));
let mut dd = DocData::Ty(TyData::from_ast(ty, qual_name, preferred_prefix));
dd.with_meta(&ty.outer_attrs);
for a in &ty.outer_attrs {
@ -200,6 +202,8 @@ impl DocData {
#[derive(Debug, Clone)]
pub struct ConstData {
pub name: String,
/// How the const is indexed, etc.
pub preferred_name: String,
/// The fully qualified name.
pub qual_name: String,
pub value: Option<String>,
@ -216,7 +220,11 @@ pub struct ConstData {
}
impl ConstData {
fn from_ast(var: &crate::parsing::ast::types::VariableDeclaration, mut qual_name: String) -> Self {
fn from_ast(
var: &crate::parsing::ast::types::VariableDeclaration,
mut qual_name: String,
preferred_prefix: &str,
) -> Self {
assert_eq!(var.kind, crate::parsing::ast::types::VariableKind::Const);
let (value, ty) = match &var.declaration.init {
@ -240,6 +248,7 @@ impl ConstData {
let name = var.declaration.id.name.clone();
qual_name.push_str(&name);
ConstData {
preferred_name: format!("{preferred_prefix}{name}"),
name,
qual_name,
value,
@ -272,7 +281,7 @@ impl ConstData {
detail.push_str(ty);
}
CompletionItem {
label: self.name.clone(),
label: self.preferred_name.clone(),
label_details: Some(CompletionItemLabelDetails {
detail: self.value.clone(),
description: None,
@ -306,6 +315,8 @@ impl ConstData {
pub struct FnData {
/// The name of the function.
pub name: String,
/// How the function is indexed, etc.
pub preferred_name: String,
/// The fully qualified name.
pub qual_name: String,
/// The args of the function.
@ -326,7 +337,11 @@ pub struct FnData {
}
impl FnData {
fn from_ast(var: &crate::parsing::ast::types::VariableDeclaration, mut qual_name: String) -> Self {
fn from_ast(
var: &crate::parsing::ast::types::VariableDeclaration,
mut qual_name: String,
preferred_prefix: &str,
) -> Self {
assert_eq!(var.kind, crate::parsing::ast::types::VariableKind::Fn);
let crate::parsing::ast::types::Expr::FunctionExpression(expr) = &var.declaration.init else {
unreachable!();
@ -345,6 +360,7 @@ impl FnData {
}
FnData {
preferred_name: format!("{preferred_prefix}{name}"),
name,
qual_name,
args: expr.params.iter().map(ArgData::from_ast).collect(),
@ -443,7 +459,7 @@ impl FnData {
}
// We end with ${} so you can jump to the end of the snippet.
// After the last argument.
format!("{}({})${{}}", self.name, args.join(", "))
format!("{}({})${{}}", self.preferred_name, args.join(", "))
}
fn to_signature_help(&self) -> SignatureHelp {
@ -452,7 +468,7 @@ impl FnData {
SignatureHelp {
signatures: vec![SignatureInformation {
label: self.name.clone(),
label: self.preferred_name.clone(),
documentation: self.short_docs().map(|s| {
Documentation::MarkupContent(MarkupContent {
kind: MarkupKind::Markdown,
@ -580,6 +596,8 @@ impl ArgKind {
pub struct TyData {
/// The name of the function.
pub name: String,
/// How the type is indexed, etc.
pub preferred_name: String,
/// The fully qualified name.
pub qual_name: String,
pub properties: Properties,
@ -597,7 +615,11 @@ pub struct TyData {
}
impl TyData {
fn from_ast(ty: &crate::parsing::ast::types::TypeDeclaration, mut qual_name: String) -> Self {
fn from_ast(
ty: &crate::parsing::ast::types::TypeDeclaration,
mut qual_name: String,
preferred_prefix: &str,
) -> Self {
let name = ty.name.name.clone();
qual_name.push_str(&name);
let mut referenced_types = HashSet::new();
@ -606,6 +628,7 @@ impl TyData {
}
TyData {
preferred_name: format!("{preferred_prefix}{name}"),
name,
qual_name,
properties: Properties {
@ -641,7 +664,7 @@ impl TyData {
fn to_completion_item(&self) -> CompletionItem {
CompletionItem {
label: self.name.clone(),
label: self.preferred_name.clone(),
label_details: self.alias.as_ref().map(|t| CompletionItemLabelDetails {
detail: Some(format!("type {} = {t}", self.name)),
description: None,
@ -658,7 +681,7 @@ impl TyData {
preselect: None,
sort_text: None,
filter_text: None,
insert_text: Some(self.name.clone()),
insert_text: Some(self.preferred_name.clone()),
insert_text_format: Some(InsertTextFormat::SNIPPET),
insert_text_mode: None,
text_edit: None,

View File

@ -6,7 +6,7 @@ use itertools::{EitherOrBoth, Itertools};
use tokio::sync::RwLock;
use crate::{
execution::{annotations, memory::Stack, EnvironmentRef, ExecState, ExecutorSettings},
execution::{annotations, memory::Stack, state::ModuleInfoMap, EnvironmentRef, ExecState, ExecutorSettings},
parsing::ast::types::{Annotation, Node, Program},
walk::Node as WalkNode,
};
@ -15,7 +15,7 @@ lazy_static::lazy_static! {
/// A static mutable lock for updating the last successful execution state for the cache.
static ref OLD_AST: Arc<RwLock<Option<OldAstState>>> = Default::default();
// The last successful run's memory. Not cleared after an unssuccessful run.
static ref PREV_MEMORY: Arc<RwLock<Option<Stack>>> = Default::default();
static ref PREV_MEMORY: Arc<RwLock<Option<(Stack, ModuleInfoMap)>>> = Default::default();
}
/// Read the old ast memory from the lock.
@ -29,12 +29,12 @@ pub(super) async fn write_old_ast(old_state: OldAstState) {
*old_ast = Some(old_state);
}
pub(crate) async fn read_old_memory() -> Option<Stack> {
pub(crate) async fn read_old_memory() -> Option<(Stack, ModuleInfoMap)> {
let old_mem = PREV_MEMORY.read().await;
old_mem.clone()
}
pub(super) async fn write_old_memory(mem: Stack) {
pub(super) async fn write_old_memory(mem: (Stack, ModuleInfoMap)) {
let mut old_mem = PREV_MEMORY.write().await;
*old_mem = Some(mem);
}

View File

@ -559,7 +559,10 @@ impl ExecutorContext {
let mut exec_state = ExecState::new(self);
if use_prev_memory {
match cache::read_old_memory().await {
Some(mem) => *exec_state.mut_stack() = mem,
Some(mem) => {
*exec_state.mut_stack() = mem.0;
exec_state.global.module_infos = mem.1;
}
None => self.prepare_mem(&mut exec_state).await?,
}
} else {
@ -577,10 +580,11 @@ impl ExecutorContext {
// memory, not to the exec_state which is not cached for mock execution.
let mut mem = exec_state.stack().clone();
let module_infos = exec_state.global.module_infos.clone();
let outcome = exec_state.to_mock_wasm_outcome(result.0).await;
mem.squash_env(result.0);
cache::write_old_memory(mem).await;
cache::write_old_memory((mem, module_infos)).await;
Ok(outcome)
}
@ -770,7 +774,7 @@ impl ExecutorContext {
if !self.is_mock() {
let mut mem = exec_state.stack().deep_clone();
mem.restore_env(env_ref);
cache::write_old_memory(mem).await;
cache::write_old_memory((mem, exec_state.global.module_infos.clone())).await;
}
let session_data = self.engine.get_session_data().await;
Ok((env_ref, session_data))

View File

@ -30,6 +30,8 @@ pub struct ExecState {
pub(super) exec_context: Option<super::ExecutorContext>,
}
pub type ModuleInfoMap = IndexMap<ModuleId, ModuleInfo>;
#[derive(Debug, Clone)]
pub(super) struct GlobalState {
/// Map from source file absolute path to module ID.
@ -37,7 +39,7 @@ pub(super) struct GlobalState {
/// Map from module ID to source file.
pub id_to_source: IndexMap<ModuleId, ModuleSource>,
/// Map from module ID to module info.
pub module_infos: IndexMap<ModuleId, ModuleInfo>,
pub module_infos: ModuleInfoMap,
/// Output map of UUIDs to artifacts.
pub artifacts: IndexMap<ArtifactId, Artifact>,
/// Output commands to allow building the artifact graph by the caller.

View File

@ -1635,7 +1635,7 @@ fn position_to_char_index(position: Position, code: &str) -> usize {
async fn with_cached_var<T>(name: &str, f: impl Fn(&KclValue) -> T) -> Option<T> {
let mem = cache::read_old_memory().await?;
let value = mem.get(name, SourceRange::default()).ok()?;
let value = mem.0.get(name, SourceRange::default()).ok()?;
Some(f(value))
}

View File

@ -88,6 +88,7 @@ pub(crate) fn read_std(mod_name: &str) -> Option<&'static str> {
"prelude" => Some(include_str!("../std/prelude.kcl")),
"math" => Some(include_str!("../std/math.kcl")),
"sketch" => Some(include_str!("../std/sketch.kcl")),
"turns" => Some(include_str!("../std/turns.kcl")),
_ => None,
}
}

View File

@ -1619,19 +1619,21 @@ impl ImportStatement {
return Some(alias.name.clone());
}
let mut parts = match &self.path {
ImportPath::Kcl { filename: s } | ImportPath::Foreign { path: s } => s.split('.'),
_ => return None,
};
let path = parts.next()?;
let _ext = parts.next()?;
let rest = parts.next();
match &self.path {
ImportPath::Kcl { filename: s } | ImportPath::Foreign { path: s } => {
let mut parts = s.split('.');
let path = parts.next()?;
let _ext = parts.next()?;
let rest = parts.next();
if rest.is_some() {
return None;
if rest.is_some() {
return None;
}
path.rsplit(&['/', '\\']).next().map(str::to_owned)
}
ImportPath::Std { path } => path.last().cloned(),
}
path.rsplit(&['/', '\\']).next().map(str::to_owned)
}
}

View File

@ -152,7 +152,12 @@ const STR_DEPRECATIONS: [(&str, &str); 6] = [
("-YZ", "-YZ"),
];
const FN_DEPRECATIONS: [(&str, &str); 3] = [("pi", "PI"), ("e", "E"), ("tau", "TAU")];
const CONST_DEPRECATIONS: [(&str, &str); 0] = [];
const CONST_DEPRECATIONS: [(&str, &str); 4] = [
("ZERO", "turns::ZERO"),
("QUARTER_TURN", "turns::QUARTER_TURN"),
("HALF_TURN", "turns::HALF_TURN"),
("THREE_QUARTER_TURN", "turns::THREE_QUARTER_TURN"),
];
#[derive(Clone, Copy)]
pub enum DeprecationKind {

View File

@ -1823,14 +1823,6 @@ fn import_stmt(i: &mut TokenSlice) -> PResult<BoxNode<ImportStatement>> {
)
.into(),
));
} else if matches!(path, ImportPath::Std { .. }) && matches!(selector, ImportSelector::None { .. }) {
return Err(ErrMode::Cut(
CompilationError::fatal(
SourceRange::new(start, end, module_id),
"the standard library cannot be imported as a part",
)
.into(),
));
}
Ok(Node::boxed(
@ -2341,21 +2333,6 @@ fn nameable_identifier(i: &mut TokenSlice) -> PResult<Node<Identifier>> {
));
}
if let Some(suggestion) = super::deprecation(&result.name, DeprecationKind::Const) {
ParseContext::warn(
CompilationError::err(
result.as_source_range(),
format!("Using `{}` is deprecated, prefer using `{}`.", result.name, suggestion),
)
.with_suggestion(
format!("Replace `{}` with `{}`", result.name, suggestion),
suggestion,
None,
Tag::Deprecated,
),
);
}
Ok(result)
}
@ -2374,8 +2351,7 @@ fn name(i: &mut TokenSlice) -> PResult<Node<Name>> {
let name = idents.pop().unwrap();
let end = name.end;
let module_id = name.module_id;
Ok(Node::new(
let result = Node::new(
Name {
name,
path: idents,
@ -2385,7 +2361,24 @@ fn name(i: &mut TokenSlice) -> PResult<Node<Name>> {
start,
end,
module_id,
))
);
if let Some(suggestion) = super::deprecation(&result.to_string(), DeprecationKind::Const) {
ParseContext::warn(
CompilationError::err(
result.as_source_range(),
format!("Using `{result}` is deprecated, prefer using `{suggestion}`."),
)
.with_suggestion(
format!("Replace `{result}` with `{suggestion}`"),
suggestion,
None,
Tag::Deprecated,
),
);
}
Ok(result)
}
impl TryFrom<Token> for Node<TagDeclarator> {