2024-09-07 12:51:35 -04:00
use std ::fmt ::Write ;
2024-12-05 17:56:49 +13:00
use crate ::parsing ::{
2024-09-07 12:51:35 -04:00
ast ::types ::{
2025-02-13 16:17:09 +13:00
Annotation , ArrayExpression , ArrayRangeExpression , BinaryExpression , BinaryOperator , BinaryPart , BodyItem ,
2025-02-27 15:58:58 +13:00
CallExpression , CallExpressionKw , CommentStyle , DefaultParamVal , Expr , FormatOptions , FunctionExpression ,
IfExpression , ImportSelector , ImportStatement , ItemVisibility , LabeledArg , Literal , LiteralIdentifier ,
LiteralValue , MemberExpression , MemberObject , Node , NonCodeNode , NonCodeValue , ObjectExpression , Parameter ,
2025-03-21 10:56:55 +13:00
PipeExpression , Program , TagDeclarator , TypeDeclaration , UnaryExpression , VariableDeclaration , VariableKind ,
2024-09-07 12:51:35 -04:00
} ,
2025-03-21 22:39:12 +13:00
deprecation ,
2025-01-31 10:45:39 -05:00
token ::NumericSuffix ,
2025-03-21 22:39:12 +13:00
DeprecationKind , PIPE_OPERATOR ,
2024-09-07 12:51:35 -04:00
} ;
impl Program {
pub fn recast ( & self , options : & FormatOptions , indentation_level : usize ) -> String {
let indentation = options . get_indentation ( indentation_level ) ;
2024-11-26 16:39:57 +13:00
2025-02-11 16:58:48 -05:00
let mut result = self
2024-11-26 16:39:57 +13:00
. shebang
. as_ref ( )
. map ( | sh | format! ( " {} \n \n " , sh . inner . content ) )
. unwrap_or_default ( ) ;
2025-03-20 16:23:20 +13:00
for start in & self . non_code_meta . start_nodes {
result . push_str ( & start . recast ( options , indentation_level ) ) ;
}
2025-02-13 16:17:09 +13:00
for attr in & self . inner_attrs {
result . push_str ( & attr . recast ( options , indentation_level ) ) ;
}
2025-03-20 16:23:20 +13:00
if ! self . inner_attrs . is_empty ( ) {
result . push ( '\n' ) ;
2025-02-11 16:58:48 -05:00
}
2025-03-20 16:23:20 +13:00
let result = result ; // Remove mutation.
2024-09-07 12:51:35 -04:00
let result = self
. body
. iter ( )
2025-02-13 16:17:09 +13:00
. map ( | body_item | {
let mut result = String ::new ( ) ;
2025-03-20 16:23:20 +13:00
for comment in body_item . get_comments ( ) {
if ! comment . is_empty ( ) {
result . push_str ( & indentation ) ;
result . push_str ( comment ) ;
}
if ! result . ends_with ( " \n \n " ) & & result ! = " \n " {
result . push ( '\n' ) ;
}
}
2025-02-13 16:17:09 +13:00
for attr in body_item . get_attrs ( ) {
result . push_str ( & attr . recast ( options , indentation_level ) ) ;
2024-09-07 12:51:35 -04:00
}
2025-02-13 16:17:09 +13:00
result . push_str ( & match body_item . clone ( ) {
BodyItem ::ImportStatement ( stmt ) = > stmt . recast ( options , indentation_level ) ,
BodyItem ::ExpressionStatement ( expression_statement ) = > {
expression_statement
. expression
2024-11-27 15:46:58 +13:00
. recast ( options , indentation_level , ExprContext ::Other )
2025-02-13 16:17:09 +13:00
}
BodyItem ::VariableDeclaration ( variable_declaration ) = > {
variable_declaration . recast ( options , indentation_level )
}
2025-03-08 03:53:34 +13:00
BodyItem ::TypeDeclaration ( ty_declaration ) = > ty_declaration . recast ( ) ,
2025-02-13 16:17:09 +13:00
BodyItem ::ReturnStatement ( return_statement ) = > {
format! (
" {}return {} " ,
indentation ,
return_statement
. argument
. recast ( options , indentation_level , ExprContext ::Other )
. trim_start ( )
)
}
} ) ;
result
2024-09-07 12:51:35 -04:00
} )
. enumerate ( )
2024-11-26 16:39:57 +13:00
. fold ( result , | mut output , ( index , recast_str ) | {
2025-02-13 16:17:09 +13:00
let start_string =
if index = = 0 & & self . non_code_meta . start_nodes . is_empty ( ) & & self . inner_attrs . is_empty ( ) {
// We need to indent.
indentation . to_string ( )
} else {
// Do nothing, we already applied the indentation elsewhere.
String ::new ( )
} ;
2024-09-07 12:51:35 -04:00
// determine the value of the end string
// basically if we are inside a nested function we want to end with a new line
let maybe_line_break : String = if index = = self . body . len ( ) - 1 & & indentation_level = = 0 {
String ::new ( )
} else {
" \n " . to_string ( )
} ;
let custom_white_space_or_comment = match self . non_code_meta . non_code_nodes . get ( & index ) {
Some ( noncodes ) = > noncodes
. iter ( )
. enumerate ( )
. map ( | ( i , custom_white_space_or_comment ) | {
2024-12-17 15:23:00 +13:00
let formatted = custom_white_space_or_comment . recast ( options , indentation_level ) ;
2024-09-07 12:51:35 -04:00
if i = = 0 & & ! formatted . trim ( ) . is_empty ( ) {
if let NonCodeValue ::BlockComment { .. } = custom_white_space_or_comment . value {
format! ( " \n {} " , formatted )
} else {
formatted
}
} else {
formatted
}
} )
. collect ::< String > ( ) ,
None = > String ::new ( ) ,
} ;
let end_string = if custom_white_space_or_comment . is_empty ( ) {
maybe_line_break
} else {
custom_white_space_or_comment
} ;
let _ = write! ( output , " {}{}{} " , start_string , recast_str , end_string ) ;
output
} )
. trim ( )
. to_string ( ) ;
// Insert a final new line if the user wants it.
if options . insert_final_newline & & ! result . is_empty ( ) {
format! ( " {} \n " , result )
} else {
result
}
}
}
impl NonCodeValue {
fn should_cause_array_newline ( & self ) -> bool {
match self {
Self ::InlineComment { .. } = > false ,
2025-02-13 16:17:09 +13:00
Self ::BlockComment { .. } | Self ::NewLineBlockComment { .. } | Self ::NewLine = > true ,
2024-12-17 15:23:00 +13:00
}
}
}
impl Node < NonCodeNode > {
fn recast ( & self , options : & FormatOptions , indentation_level : usize ) -> String {
let indentation = options . get_indentation ( indentation_level ) ;
match & self . value {
NonCodeValue ::InlineComment {
value ,
style : CommentStyle ::Line ,
} = > format! ( " // {} \n " , value ) ,
NonCodeValue ::InlineComment {
value ,
style : CommentStyle ::Block ,
} = > format! ( " /* {} */ " , value ) ,
NonCodeValue ::BlockComment { value , style } = > match style {
CommentStyle ::Block = > format! ( " {} /* {} */ " , indentation , value ) ,
CommentStyle ::Line = > {
if value . trim ( ) . is_empty ( ) {
format! ( " {} // \n " , indentation )
} else {
format! ( " {} // {} \n " , indentation , value . trim ( ) )
}
}
} ,
NonCodeValue ::NewLineBlockComment { value , style } = > {
let add_start_new_line = if self . start = = 0 { " " } else { " \n \n " } ;
match style {
CommentStyle ::Block = > format! ( " {} {} /* {} */ \n " , add_start_new_line , indentation , value ) ,
CommentStyle ::Line = > {
if value . trim ( ) . is_empty ( ) {
format! ( " {} {} // \n " , add_start_new_line , indentation )
} else {
format! ( " {} {} // {} \n " , add_start_new_line , indentation , value . trim ( ) )
}
}
}
}
NonCodeValue ::NewLine = > " \n \n " . to_string ( ) ,
2025-02-13 16:17:09 +13:00
}
}
}
2024-12-17 15:23:00 +13:00
2025-02-13 16:17:09 +13:00
impl Node < Annotation > {
fn recast ( & self , options : & FormatOptions , indentation_level : usize ) -> String {
2025-03-20 16:23:20 +13:00
let indentation = options . get_indentation ( indentation_level ) ;
let mut result = String ::new ( ) ;
for comment in & self . pre_comments {
if ! comment . is_empty ( ) {
result . push_str ( & indentation ) ;
result . push_str ( comment ) ;
}
2025-04-11 15:19:52 +12:00
if ! result . ends_with ( " \n \n " ) & & result ! = " \n " {
2025-03-20 16:23:20 +13:00
result . push ( '\n' ) ;
}
}
result . push ( '@' ) ;
2025-02-13 16:17:09 +13:00
if let Some ( name ) = & self . name {
result . push_str ( & name . name ) ;
2024-09-07 12:51:35 -04:00
}
2025-02-13 16:17:09 +13:00
if let Some ( properties ) = & self . properties {
result . push ( '(' ) ;
result . push_str (
& properties
. iter ( )
. map ( | prop | {
format! (
" {} = {} " ,
prop . key . name ,
prop . value
. recast ( options , indentation_level + 1 , ExprContext ::Other )
. trim ( )
)
} )
. collect ::< Vec < String > > ( )
. join ( " , " ) ,
) ;
result . push ( ')' ) ;
result . push ( '\n' ) ;
}
result
2024-09-07 12:51:35 -04:00
}
}
2024-10-17 00:48:33 -04:00
impl ImportStatement {
pub fn recast ( & self , options : & FormatOptions , indentation_level : usize ) -> String {
let indentation = options . get_indentation ( indentation_level ) ;
2024-12-07 07:16:04 +13:00
let vis = if self . visibility = = ItemVisibility ::Export {
" export "
} else {
" "
} ;
let mut string = format! ( " {} {} import " , vis , indentation ) ;
match & self . selector {
ImportSelector ::List { items } = > {
for ( i , item ) in items . iter ( ) . enumerate ( ) {
if i > 0 {
string . push_str ( " , " ) ;
}
string . push_str ( & item . name . name ) ;
if let Some ( alias ) = & item . alias {
// If the alias is the same, don't output it.
if item . name . name ! = alias . name {
string . push_str ( & format! ( " as {} " , alias . name ) ) ;
}
}
2024-10-17 00:48:33 -04:00
}
2024-12-07 07:16:04 +13:00
string . push_str ( " from " ) ;
2024-10-17 00:48:33 -04:00
}
2024-12-07 07:16:04 +13:00
ImportSelector ::Glob ( _ ) = > string . push_str ( " * from " ) ,
2024-12-17 09:38:32 +13:00
ImportSelector ::None { .. } = > { }
2024-12-07 07:16:04 +13:00
}
string . push_str ( & format! ( " \" {} \" " , self . path ) ) ;
2024-12-17 09:38:32 +13:00
if let ImportSelector ::None { alias : Some ( alias ) } = & self . selector {
2024-12-07 07:16:04 +13:00
string . push_str ( " as " ) ;
string . push_str ( & alias . name ) ;
2024-10-17 00:48:33 -04:00
}
string
}
}
2024-11-27 15:46:58 +13:00
#[ derive(Copy, Clone, Debug, Eq, PartialEq) ]
pub ( crate ) enum ExprContext {
Pipe ,
Decl ,
Other ,
}
2024-09-07 12:51:35 -04:00
impl Expr {
Bug: KCL formatter removes 'fn' from closures: (#4718)
# Problem
Before this PR, our formatter reformats
```
squares_out = reduce(arr, 0, fn (i, squares) {
return 1
})
```
to
```
squares_out = reduce(arr, 0, (i, squares) {
return 1
})
```
i.e. it removes the `fn` keyword from the closure. This keyword is required, so, our formatter turned working code into invalid code.
# Cause
When this closure parameter is formatted, the ExprContext is ::Decl, so `Expr::recast` skips adding the `fn` keyword. The reason it's ::Decl is because the `squares_out = ` declaration sets it, and no subsequent call sets the context to something else.
# Solution
When recasting a call expression, set the context for every argument to `ExprContext::Other`.
2024-12-09 19:13:49 -06:00
pub ( crate ) fn recast ( & self , options : & FormatOptions , indentation_level : usize , mut ctxt : ExprContext ) -> String {
let is_decl = matches! ( ctxt , ExprContext ::Decl ) ;
if is_decl {
// Just because this expression is being bound to a variable, doesn't mean that every child
// expression is being bound. So, reset the expression context if necessary.
// This will still preserve the "::Pipe" context though.
ctxt = ExprContext ::Other ;
}
2024-09-07 12:51:35 -04:00
match & self {
Expr ::BinaryExpression ( bin_exp ) = > bin_exp . recast ( options ) ,
2024-11-27 15:46:58 +13:00
Expr ::ArrayExpression ( array_exp ) = > array_exp . recast ( options , indentation_level , ctxt ) ,
Expr ::ArrayRangeExpression ( range_exp ) = > range_exp . recast ( options , indentation_level , ctxt ) ,
Expr ::ObjectExpression ( ref obj_exp ) = > obj_exp . recast ( options , indentation_level , ctxt ) ,
2024-09-07 12:51:35 -04:00
Expr ::MemberExpression ( mem_exp ) = > mem_exp . recast ( ) ,
Expr ::Literal ( literal ) = > literal . recast ( ) ,
2024-11-27 15:46:58 +13:00
Expr ::FunctionExpression ( func_exp ) = > {
Bug: KCL formatter removes 'fn' from closures: (#4718)
# Problem
Before this PR, our formatter reformats
```
squares_out = reduce(arr, 0, fn (i, squares) {
return 1
})
```
to
```
squares_out = reduce(arr, 0, (i, squares) {
return 1
})
```
i.e. it removes the `fn` keyword from the closure. This keyword is required, so, our formatter turned working code into invalid code.
# Cause
When this closure parameter is formatted, the ExprContext is ::Decl, so `Expr::recast` skips adding the `fn` keyword. The reason it's ::Decl is because the `squares_out = ` declaration sets it, and no subsequent call sets the context to something else.
# Solution
When recasting a call expression, set the context for every argument to `ExprContext::Other`.
2024-12-09 19:13:49 -06:00
let mut result = if is_decl { String ::new ( ) } else { " fn " . to_owned ( ) } ;
2024-11-27 15:46:58 +13:00
result + = & func_exp . recast ( options , indentation_level ) ;
result
}
Expr ::CallExpression ( call_exp ) = > call_exp . recast ( options , indentation_level , ctxt ) ,
2024-12-02 15:23:18 -06:00
Expr ::CallExpressionKw ( call_exp ) = > call_exp . recast ( options , indentation_level , ctxt ) ,
2025-03-24 20:58:55 +13:00
Expr ::Name ( name ) = > {
let result = name . to_string ( ) ;
match deprecation ( & result , DeprecationKind ::Const ) {
Some ( suggestion ) = > suggestion . to_owned ( ) ,
None = > result ,
}
}
2024-09-07 12:51:35 -04:00
Expr ::TagDeclarator ( tag ) = > tag . recast ( ) ,
Expr ::PipeExpression ( pipe_exp ) = > pipe_exp . recast ( options , indentation_level ) ,
Expr ::UnaryExpression ( unary_exp ) = > unary_exp . recast ( options ) ,
2024-11-27 15:46:58 +13:00
Expr ::IfExpression ( e ) = > e . recast ( options , indentation_level , ctxt ) ,
2024-12-05 17:56:49 +13:00
Expr ::PipeSubstitution ( _ ) = > crate ::parsing ::PIPE_SUBSTITUTION_OPERATOR . to_string ( ) ,
2024-12-11 21:26:42 +13:00
Expr ::LabelledExpression ( e ) = > {
let mut result = e . expr . recast ( options , indentation_level , ctxt ) ;
result + = " as " ;
result + = & e . label . name ;
result
}
2025-02-27 15:58:58 +13:00
Expr ::AscribedExpression ( e ) = > {
let mut result = e . expr . recast ( options , indentation_level , ctxt ) ;
result + = " : " ;
2025-03-21 10:56:55 +13:00
result + = & e . ty . to_string ( ) ;
2025-02-27 15:58:58 +13:00
result
}
2024-09-07 12:51:35 -04:00
Expr ::None ( _ ) = > {
unimplemented! ( " there is no literal None, see https://github.com/KittyCAD/modeling-app/issues/1115 " )
}
}
}
}
impl BinaryPart {
fn recast ( & self , options : & FormatOptions , indentation_level : usize ) -> String {
match & self {
BinaryPart ::Literal ( literal ) = > literal . recast ( ) ,
2025-03-24 20:58:55 +13:00
BinaryPart ::Name ( name ) = > {
let result = name . to_string ( ) ;
match deprecation ( & result , DeprecationKind ::Const ) {
Some ( suggestion ) = > suggestion . to_owned ( ) ,
None = > result ,
}
}
2024-09-07 12:51:35 -04:00
BinaryPart ::BinaryExpression ( binary_expression ) = > binary_expression . recast ( options ) ,
2024-11-27 15:46:58 +13:00
BinaryPart ::CallExpression ( call_expression ) = > {
call_expression . recast ( options , indentation_level , ExprContext ::Other )
}
2024-12-02 15:23:18 -06:00
BinaryPart ::CallExpressionKw ( call_expression ) = > {
call_expression . recast ( options , indentation_level , ExprContext ::Other )
}
2024-09-07 12:51:35 -04:00
BinaryPart ::UnaryExpression ( unary_expression ) = > unary_expression . recast ( options ) ,
BinaryPart ::MemberExpression ( member_expression ) = > member_expression . recast ( ) ,
2024-11-27 15:46:58 +13:00
BinaryPart ::IfExpression ( e ) = > e . recast ( options , indentation_level , ExprContext ::Other ) ,
2024-09-07 12:51:35 -04:00
}
}
}
impl CallExpression {
2024-11-27 15:46:58 +13:00
fn recast ( & self , options : & FormatOptions , indentation_level : usize , ctxt : ExprContext ) -> String {
2024-09-07 12:51:35 -04:00
format! (
" {}{}({}) " ,
2024-11-27 15:46:58 +13:00
if ctxt = = ExprContext ::Pipe {
2024-09-07 12:51:35 -04:00
" " . to_string ( )
} else {
options . get_indentation ( indentation_level )
} ,
2025-03-24 20:58:55 +13:00
self . callee ,
2024-09-07 12:51:35 -04:00
self . arguments
. iter ( )
2024-11-27 15:46:58 +13:00
. map ( | arg | arg . recast ( options , indentation_level , ctxt ) )
2024-09-07 12:51:35 -04:00
. collect ::< Vec < String > > ( )
. join ( " , " )
)
}
}
2024-12-02 15:23:18 -06:00
impl CallExpressionKw {
2025-02-21 14:41:25 -06:00
fn recast_args ( & self , options : & FormatOptions , indentation_level : usize , ctxt : ExprContext ) -> Vec < String > {
2024-12-02 15:23:18 -06:00
let mut arg_list = if let Some ( first_arg ) = & self . unlabeled {
vec! [ first_arg . recast ( options , indentation_level , ctxt ) ]
} else {
2025-02-21 14:41:25 -06:00
Vec ::with_capacity ( self . arguments . len ( ) )
2024-12-02 15:23:18 -06:00
} ;
arg_list . extend (
self . arguments
. iter ( )
. map ( | arg | arg . recast ( options , indentation_level , ctxt ) ) ,
) ;
2025-02-21 14:41:25 -06:00
arg_list
}
fn recast ( & self , options : & FormatOptions , indentation_level : usize , ctxt : ExprContext ) -> String {
let indent = if ctxt = = ExprContext ::Pipe {
" " . to_string ( )
} else {
options . get_indentation ( indentation_level )
} ;
2025-03-24 20:58:55 +13:00
let name = self . callee . to_string ( ) ;
2025-03-21 22:39:12 +13:00
2025-03-24 20:58:55 +13:00
if let Some ( suggestion ) = deprecation ( & name , DeprecationKind ::Function ) {
2025-03-21 22:39:12 +13:00
return format! ( " {indent} {suggestion} " ) ;
}
2025-02-21 14:41:25 -06:00
let arg_list = self . recast_args ( options , indentation_level , ctxt ) ;
2025-02-11 16:06:47 -06:00
let args = arg_list . clone ( ) . join ( " , " ) ;
2025-02-21 14:41:25 -06:00
let has_lots_of_args = arg_list . len ( ) > = 4 ;
let some_arg_is_already_multiline = arg_list . len ( ) > 1 & & arg_list . iter ( ) . any ( | arg | arg . contains ( '\n' ) ) ;
let multiline = has_lots_of_args | | some_arg_is_already_multiline ;
if multiline {
let next_indent = indentation_level + 1 ;
2025-02-11 16:06:47 -06:00
let inner_indentation = if ctxt = = ExprContext ::Pipe {
2025-02-21 14:41:25 -06:00
options . get_indentation_offset_pipe ( next_indent )
2025-02-11 16:06:47 -06:00
} else {
2025-02-21 14:41:25 -06:00
options . get_indentation ( next_indent )
2025-02-11 16:06:47 -06:00
} ;
2025-02-21 14:41:25 -06:00
let arg_list = self . recast_args ( options , next_indent , ctxt ) ;
2025-02-11 16:06:47 -06:00
let mut args = arg_list . join ( & format! ( " , \n {inner_indentation} " ) ) ;
args . push ( ',' ) ;
let args = args ;
let end_indent = if ctxt = = ExprContext ::Pipe {
options . get_indentation_offset_pipe ( indentation_level )
} else {
options . get_indentation ( indentation_level )
} ;
format! ( " {indent} {name} ( \n {inner_indentation} {args} \n {end_indent} ) " )
} else {
format! ( " {indent} {name} ( {args} ) " )
}
2024-12-02 15:23:18 -06:00
}
}
impl LabeledArg {
fn recast ( & self , options : & FormatOptions , indentation_level : usize , ctxt : ExprContext ) -> String {
let label = & self . label . name ;
let arg = self . arg . recast ( options , indentation_level , ctxt ) ;
2024-12-12 11:53:35 -06:00
format! ( " {label} = {arg} " )
2024-12-02 15:23:18 -06:00
}
}
2024-09-07 12:51:35 -04:00
impl VariableDeclaration {
pub fn recast ( & self , options : & FormatOptions , indentation_level : usize ) -> String {
let indentation = options . get_indentation ( indentation_level ) ;
2024-12-07 07:16:04 +13:00
let mut output = match self . visibility {
2024-10-17 00:48:33 -04:00
ItemVisibility ::Default = > String ::new ( ) ,
ItemVisibility ::Export = > " export " . to_owned ( ) ,
} ;
2024-12-07 07:16:04 +13:00
let ( keyword , eq ) = match self . kind {
VariableKind ::Fn = > ( " fn " , " " ) ,
VariableKind ::Const = > ( " " , " = " ) ,
} ;
let _ = write! (
output ,
" {}{keyword}{}{eq}{} " ,
indentation ,
self . declaration . id . name ,
self . declaration
. init
. recast ( options , indentation_level , ExprContext ::Decl )
. trim ( )
) ;
output
2024-09-07 12:51:35 -04:00
}
}
2025-03-08 03:53:34 +13:00
impl TypeDeclaration {
pub fn recast ( & self ) -> String {
let vis = match self . visibility {
ItemVisibility ::Default = > String ::new ( ) ,
ItemVisibility ::Export = > " export " . to_owned ( ) ,
} ;
let mut arg_str = String ::new ( ) ;
if let Some ( args ) = & self . args {
arg_str . push ( '(' ) ;
for a in args {
if arg_str . len ( ) > 1 {
arg_str . push_str ( " , " ) ;
}
arg_str . push_str ( & a . name ) ;
}
arg_str . push ( ')' ) ;
}
2025-03-21 10:56:55 +13:00
if let Some ( alias ) = & self . alias {
arg_str . push_str ( " = " ) ;
arg_str . push_str ( & alias . to_string ( ) ) ;
}
2025-03-08 03:53:34 +13:00
format! ( " {} type {} {} " , vis , self . name . name , arg_str )
}
}
2025-01-31 10:45:39 -05:00
// Used by TS.
pub fn format_number ( value : f64 , suffix : NumericSuffix ) -> String {
format! ( " {value} {suffix} " )
}
2024-09-07 12:51:35 -04:00
impl Literal {
fn recast ( & self ) -> String {
match self . value {
2025-01-22 08:29:30 +13:00
LiteralValue ::Number { value , suffix } = > {
if self . raw . contains ( '.' ) & & value . fract ( ) = = 0.0 {
format! ( " {value:?} {suffix} " )
2024-09-07 12:51:35 -04:00
} else {
2025-03-09 03:33:45 +13:00
self . raw . clone ( )
2024-09-07 12:51:35 -04:00
}
}
LiteralValue ::String ( ref s ) = > {
2025-03-21 22:39:12 +13:00
if let Some ( suggestion ) = deprecation ( s , DeprecationKind ::String ) {
return suggestion . to_owned ( ) ;
}
2024-09-07 12:51:35 -04:00
let quote = if self . raw . trim ( ) . starts_with ( '"' ) { '"' } else { '\'' } ;
format! ( " {quote} {s} {quote} " )
}
LiteralValue ::Bool ( _ ) = > self . raw . clone ( ) ,
}
}
}
impl TagDeclarator {
pub fn recast ( & self ) -> String {
// TagDeclarators are always prefixed with a dollar sign.
format! ( " $ {} " , self . name )
}
}
impl ArrayExpression {
2024-11-27 15:46:58 +13:00
fn recast ( & self , options : & FormatOptions , indentation_level : usize , ctxt : ExprContext ) -> String {
2024-09-07 12:51:35 -04:00
// Reconstruct the order of items in the array.
// An item can be an element (i.e. an expression for a KCL value),
// or a non-code item (e.g. a comment)
let num_items = self . elements . len ( ) + self . non_code_meta . non_code_nodes_len ( ) ;
let mut elems = self . elements . iter ( ) ;
let mut found_line_comment = false ;
let mut format_items : Vec < _ > = ( 0 .. num_items )
. flat_map ( | i | {
if let Some ( noncode ) = self . non_code_meta . non_code_nodes . get ( & i ) {
noncode
. iter ( )
. map ( | nc | {
found_line_comment | = nc . value . should_cause_array_newline ( ) ;
2024-12-17 15:23:00 +13:00
nc . recast ( options , 0 )
2024-09-07 12:51:35 -04:00
} )
. collect ::< Vec < _ > > ( )
} else {
let el = elems . next ( ) . unwrap ( ) ;
2024-11-27 15:46:58 +13:00
let s = format! ( " {} , " , el . recast ( options , 0 , ExprContext ::Other ) ) ;
2024-09-07 12:51:35 -04:00
vec! [ s ]
}
} )
. collect ( ) ;
// Format these items into a one-line array.
if let Some ( item ) = format_items . last_mut ( ) {
if let Some ( norm ) = item . strip_suffix ( " , " ) {
* item = norm . to_owned ( ) ;
}
}
let format_items = format_items ; // Remove mutability
let flat_recast = format! ( " [ {} ] " , format_items . join ( " " ) ) ;
// We might keep the one-line representation, if it's short enough.
let max_array_length = 40 ;
let multi_line = flat_recast . len ( ) > max_array_length | | found_line_comment ;
if ! multi_line {
return flat_recast ;
}
// Otherwise, we format a multi-line representation.
2024-11-27 15:46:58 +13:00
let inner_indentation = if ctxt = = ExprContext ::Pipe {
2024-09-07 12:51:35 -04:00
options . get_indentation_offset_pipe ( indentation_level + 1 )
} else {
options . get_indentation ( indentation_level + 1 )
} ;
let formatted_array_lines = format_items
. iter ( )
. map ( | s | {
format! (
" {inner_indentation}{}{} " ,
if let Some ( x ) = s . strip_suffix ( " " ) { x } else { s } ,
if s . ends_with ( '\n' ) { " " } else { " \n " }
)
} )
. collect ::< Vec < String > > ( )
. join ( " " )
. to_owned ( ) ;
2024-11-27 15:46:58 +13:00
let end_indent = if ctxt = = ExprContext ::Pipe {
2024-09-07 12:51:35 -04:00
options . get_indentation_offset_pipe ( indentation_level )
} else {
options . get_indentation ( indentation_level )
} ;
format! ( " [ \n {formatted_array_lines} {end_indent} ] " )
}
}
2024-10-17 02:58:04 +13:00
/// An expression is syntactically trivial: i.e., a literal, identifier, or similar.
fn expr_is_trivial ( expr : & Expr ) -> bool {
2025-02-27 15:58:58 +13:00
matches! (
expr ,
2025-03-24 20:58:55 +13:00
Expr ::Literal ( _ ) | Expr ::Name ( _ ) | Expr ::TagDeclarator ( _ ) | Expr ::PipeSubstitution ( _ ) | Expr ::None ( _ )
2025-02-27 15:58:58 +13:00
)
2024-10-17 02:58:04 +13:00
}
impl ArrayRangeExpression {
2024-11-27 15:46:58 +13:00
fn recast ( & self , options : & FormatOptions , _ : usize , _ : ExprContext ) -> String {
let s1 = self . start_element . recast ( options , 0 , ExprContext ::Other ) ;
let s2 = self . end_element . recast ( options , 0 , ExprContext ::Other ) ;
2024-10-17 02:58:04 +13:00
// Format these items into a one-line array. Put spaces around the `..` if either expression
// is non-trivial. This is a bit arbitrary but people seem to like simple ranges to be formatted
// tightly, but this is a misleading visual representation of the precedence if the range
// components are compound expressions.
if expr_is_trivial ( & self . start_element ) & & expr_is_trivial ( & self . end_element ) {
format! ( " [ {s1} .. {s2} ] " )
} else {
format! ( " [ {s1} .. {s2} ] " )
}
// Assume a range expression fits on one line.
}
}
2024-09-07 12:51:35 -04:00
impl ObjectExpression {
2024-11-27 15:46:58 +13:00
fn recast ( & self , options : & FormatOptions , indentation_level : usize , ctxt : ExprContext ) -> String {
2024-09-07 12:51:35 -04:00
if self
. non_code_meta
. non_code_nodes
. values ( )
. any ( | nc | nc . iter ( ) . any ( | nc | nc . value . should_cause_array_newline ( ) ) )
{
2024-11-27 15:46:58 +13:00
return self . recast_multi_line ( options , indentation_level , ctxt ) ;
2024-09-07 12:51:35 -04:00
}
let flat_recast = format! (
" {{ {} }} " ,
self . properties
. iter ( )
. map ( | prop | {
format! (
2024-11-25 09:21:55 +13:00
" {} = {} " ,
2024-09-07 12:51:35 -04:00
prop . key . name ,
2024-11-27 15:46:58 +13:00
prop . value . recast ( options , indentation_level + 1 , ctxt ) . trim ( )
2024-09-07 12:51:35 -04:00
)
} )
. collect ::< Vec < String > > ( )
. join ( " , " )
) ;
let max_array_length = 40 ;
let needs_multiple_lines = flat_recast . len ( ) > max_array_length ;
if ! needs_multiple_lines {
return flat_recast ;
}
2024-11-27 15:46:58 +13:00
self . recast_multi_line ( options , indentation_level , ctxt )
2024-09-07 12:51:35 -04:00
}
/// Recast, but always outputs the object with newlines between each property.
2024-11-27 15:46:58 +13:00
fn recast_multi_line ( & self , options : & FormatOptions , indentation_level : usize , ctxt : ExprContext ) -> String {
let inner_indentation = if ctxt = = ExprContext ::Pipe {
2024-09-07 12:51:35 -04:00
options . get_indentation_offset_pipe ( indentation_level + 1 )
} else {
options . get_indentation ( indentation_level + 1 )
} ;
let num_items = self . properties . len ( ) + self . non_code_meta . non_code_nodes_len ( ) ;
let mut props = self . properties . iter ( ) ;
let format_items : Vec < _ > = ( 0 .. num_items )
. flat_map ( | i | {
if let Some ( noncode ) = self . non_code_meta . non_code_nodes . get ( & i ) {
2024-12-17 15:23:00 +13:00
noncode . iter ( ) . map ( | nc | nc . recast ( options , 0 ) ) . collect ::< Vec < _ > > ( )
2024-09-07 12:51:35 -04:00
} else {
let prop = props . next ( ) . unwrap ( ) ;
// Use a comma unless it's the last item
let comma = if i = = num_items - 1 { " " } else { " , \n " } ;
let s = format! (
2024-11-25 09:21:55 +13:00
" {} = {}{comma} " ,
2024-09-07 12:51:35 -04:00
prop . key . name ,
2024-11-27 15:46:58 +13:00
prop . value . recast ( options , indentation_level + 1 , ctxt ) . trim ( )
2024-09-07 12:51:35 -04:00
) ;
vec! [ s ]
}
} )
. collect ( ) ;
2024-11-27 15:46:58 +13:00
let end_indent = if ctxt = = ExprContext ::Pipe {
2024-09-07 12:51:35 -04:00
options . get_indentation_offset_pipe ( indentation_level )
} else {
options . get_indentation ( indentation_level )
} ;
format! (
" {{ \n {inner_indentation}{} \n {end_indent}}} " ,
format_items . join ( & inner_indentation ) ,
)
}
}
impl MemberExpression {
fn recast ( & self ) -> String {
let key_str = match & self . property {
LiteralIdentifier ::Identifier ( identifier ) = > {
if self . computed {
format! ( " [ {} ] " , & ( * identifier . name ) )
} else {
format! ( " . {} " , & ( * identifier . name ) )
}
}
LiteralIdentifier ::Literal ( lit ) = > format! ( " [ {} ] " , & ( * lit . raw ) ) ,
} ;
match & self . object {
MemberObject ::MemberExpression ( member_exp ) = > member_exp . recast ( ) + key_str . as_str ( ) ,
MemberObject ::Identifier ( identifier ) = > identifier . name . to_string ( ) + key_str . as_str ( ) ,
}
}
}
impl BinaryExpression {
fn recast ( & self , options : & FormatOptions ) -> String {
let maybe_wrap_it = | a : String , doit : bool | -> String {
if doit {
format! ( " ( {} ) " , a )
} else {
a
}
} ;
let should_wrap_right = match & self . right {
BinaryPart ::BinaryExpression ( bin_exp ) = > {
self . precedence ( ) > bin_exp . precedence ( )
| | self . operator = = BinaryOperator ::Sub
| | self . operator = = BinaryOperator ::Div
}
_ = > false ,
} ;
let should_wrap_left = match & self . left {
BinaryPart ::BinaryExpression ( bin_exp ) = > self . precedence ( ) > bin_exp . precedence ( ) ,
_ = > false ,
} ;
format! (
" {} {} {} " ,
maybe_wrap_it ( self . left . recast ( options , 0 ) , should_wrap_left ) ,
self . operator ,
maybe_wrap_it ( self . right . recast ( options , 0 ) , should_wrap_right )
)
}
}
impl UnaryExpression {
fn recast ( & self , options : & FormatOptions ) -> String {
match self . argument {
BinaryPart ::Literal ( _ )
2025-03-24 20:58:55 +13:00
| BinaryPart ::Name ( _ )
2024-09-07 12:51:35 -04:00
| BinaryPart ::MemberExpression ( _ )
2024-09-30 15:40:50 -05:00
| BinaryPart ::IfExpression ( _ )
2024-12-02 15:23:18 -06:00
| BinaryPart ::CallExpressionKw ( _ )
2024-09-07 12:51:35 -04:00
| BinaryPart ::CallExpression ( _ ) = > {
format! ( " {} {} " , & self . operator , self . argument . recast ( options , 0 ) )
}
BinaryPart ::BinaryExpression ( _ ) | BinaryPart ::UnaryExpression ( _ ) = > {
format! ( " {} ( {} ) " , & self . operator , self . argument . recast ( options , 0 ) )
}
}
}
}
2024-09-30 15:40:50 -05:00
impl IfExpression {
2024-11-27 15:46:58 +13:00
fn recast ( & self , options : & FormatOptions , indentation_level : usize , ctxt : ExprContext ) -> String {
2024-09-30 15:40:50 -05:00
// We can calculate how many lines this will take, so let's do it and avoid growing the vec.
// Total lines = starting lines, else-if lines, ending lines.
let n = 2 + ( self . else_ifs . len ( ) * 2 ) + 3 ;
let mut lines = Vec ::with_capacity ( n ) ;
2024-11-27 15:46:58 +13:00
let cond = self . cond . recast ( options , indentation_level , ctxt ) ;
2024-09-30 15:40:50 -05:00
lines . push ( ( 0 , format! ( " if {cond} {{ " ) ) ) ;
lines . push ( ( 1 , self . then_val . recast ( options , indentation_level + 1 ) ) ) ;
for else_if in & self . else_ifs {
2024-11-27 15:46:58 +13:00
let cond = else_if . cond . recast ( options , indentation_level , ctxt ) ;
2024-09-30 15:40:50 -05:00
lines . push ( ( 0 , format! ( " }} else if {cond} {{ " ) ) ) ;
lines . push ( ( 1 , else_if . then_val . recast ( options , indentation_level + 1 ) ) ) ;
}
lines . push ( ( 0 , " } else { " . to_owned ( ) ) ) ;
lines . push ( ( 1 , self . final_else . recast ( options , indentation_level + 1 ) ) ) ;
lines . push ( ( 0 , " } " . to_owned ( ) ) ) ;
lines
. into_iter ( )
. map ( | ( ind , line ) | format! ( " {} {} " , options . get_indentation ( indentation_level + ind ) , line . trim ( ) ) )
. collect ::< Vec < _ > > ( )
. join ( " \n " )
}
}
2024-10-30 16:52:17 -04:00
impl Node < PipeExpression > {
2024-09-07 12:51:35 -04:00
fn recast ( & self , options : & FormatOptions , indentation_level : usize ) -> String {
let pipe = self
. body
. iter ( )
. enumerate ( )
. map ( | ( index , statement ) | {
let indentation = options . get_indentation ( indentation_level + 1 ) ;
2024-11-27 15:46:58 +13:00
let mut s = statement . recast ( options , indentation_level + 1 , ExprContext ::Pipe ) ;
2024-09-07 12:51:35 -04:00
let non_code_meta = self . non_code_meta . clone ( ) ;
if let Some ( non_code_meta_value ) = non_code_meta . non_code_nodes . get ( & index ) {
for val in non_code_meta_value {
let formatted = if val . end = = self . end {
2024-12-17 15:23:00 +13:00
val . recast ( options , indentation_level )
. trim_end_matches ( '\n' )
. to_string ( )
2024-09-07 12:51:35 -04:00
} else {
2024-12-17 15:23:00 +13:00
val . recast ( options , indentation_level + 1 )
. trim_end_matches ( '\n' )
. to_string ( )
2024-09-07 12:51:35 -04:00
} ;
if let NonCodeValue ::BlockComment { .. } = val . value {
s + = " \n " ;
s + = & formatted ;
} else {
s + = & formatted ;
}
}
}
if index ! = self . body . len ( ) - 1 {
s + = " \n " ;
s + = & indentation ;
s + = PIPE_OPERATOR ;
s + = " " ;
}
s
} )
. collect ::< String > ( ) ;
format! ( " {} {} " , options . get_indentation ( indentation_level ) , pipe )
}
}
impl FunctionExpression {
pub fn recast ( & self , options : & FormatOptions , indentation_level : usize ) -> String {
// We don't want to end with a new line inside nested functions.
let mut new_options = options . clone ( ) ;
new_options . insert_final_newline = false ;
2024-11-20 08:23:30 -06:00
let param_list = self
. params
. iter ( )
2024-11-27 15:46:58 +13:00
. map ( | param | param . recast ( options , indentation_level ) )
2024-11-20 08:23:30 -06:00
. collect ::< Vec < String > > ( )
. join ( " , " ) ;
let tab0 = options . get_indentation ( indentation_level ) ;
let tab1 = options . get_indentation ( indentation_level + 1 ) ;
2024-11-27 15:46:58 +13:00
let return_type = match & self . return_type {
2025-03-21 10:56:55 +13:00
Some ( rt ) = > format! ( " : {rt} " ) ,
2024-11-27 15:46:58 +13:00
None = > String ::new ( ) ,
} ;
2024-11-20 08:23:30 -06:00
let body = self . body . recast ( & new_options , indentation_level + 1 ) ;
2024-11-27 15:46:58 +13:00
format! ( " ( {param_list} ) {return_type} {{ \n {tab1} {body} \n {tab0} }} " )
}
}
impl Parameter {
2025-03-21 10:56:55 +13:00
pub fn recast ( & self , _options : & FormatOptions , _indentation_level : usize ) -> String {
2024-12-10 10:20:51 -06:00
let at_sign = if self . labeled { " " } else { " @ " } ;
let identifier = & self . identifier . name ;
let question_mark = if self . default_value . is_some ( ) { " ? " } else { " " } ;
let mut result = format! ( " {at_sign} {identifier} {question_mark} " ) ;
2024-11-27 15:46:58 +13:00
if let Some ( ty ) = & self . type_ {
result + = " : " ;
2025-03-21 10:56:55 +13:00
result + = & ty . to_string ( ) ;
2024-11-27 15:46:58 +13:00
}
2024-12-10 10:20:51 -06:00
if let Some ( DefaultParamVal ::Literal ( ref literal ) ) = self . default_value {
let lit = literal . recast ( ) ;
result . push_str ( & format! ( " = {lit} " ) ) ;
} ;
2024-11-27 15:46:58 +13:00
result
}
}
2025-03-29 19:26:20 -07:00
/// Collect all the kcl (and other relevant) files in a directory, recursively.
2025-03-17 15:55:25 -07:00
#[ cfg(not(target_arch = " wasm32 " )) ]
#[ async_recursion::async_recursion ]
2025-03-29 19:26:20 -07:00
pub async fn walk_dir ( dir : & std ::path ::PathBuf ) -> Result < Vec < std ::path ::PathBuf > , anyhow ::Error > {
2025-03-17 15:55:25 -07:00
// Make sure we actually have a directory.
if ! dir . is_dir ( ) {
anyhow ::bail! ( " `{}` is not a directory " , dir . display ( ) ) ;
}
let mut entries = tokio ::fs ::read_dir ( dir ) . await ? ;
let mut files = Vec ::new ( ) ;
while let Some ( entry ) = entries . next_entry ( ) . await ? {
let path = entry . path ( ) ;
if path . is_dir ( ) {
files . extend ( walk_dir ( & path ) . await ? ) ;
2025-03-29 19:26:20 -07:00
} else if path
. extension ( )
2025-04-10 13:57:12 -07:00
. is_some_and ( | ext | crate ::RELEVANT_FILE_EXTENSIONS . contains ( & ext . to_string_lossy ( ) . to_string ( ) ) )
2025-03-29 19:26:20 -07:00
{
2025-03-17 15:55:25 -07:00
files . push ( path ) ;
}
}
Ok ( files )
}
/// Recast all the kcl files in a directory, recursively.
#[ cfg(not(target_arch = " wasm32 " )) ]
2025-03-19 16:43:10 -07:00
pub async fn recast_dir ( dir : & std ::path ::Path , options : & crate ::FormatOptions ) -> Result < ( ) , anyhow ::Error > {
2025-03-17 15:55:25 -07:00
let files = walk_dir ( & dir . to_path_buf ( ) ) . await . map_err ( | err | {
crate ::KclError ::Internal ( crate ::errors ::KclErrorDetails {
message : format ! ( " Failed to walk directory `{}`: {:?} " , dir . display ( ) , err ) ,
source_ranges : vec ! [ crate ::SourceRange ::default ( ) ] ,
} )
} ) ? ;
let futures = files
. into_iter ( )
2025-03-29 19:26:20 -07:00
. filter ( | file | file . extension ( ) . is_some_and ( | ext | ext = = " kcl " ) ) // We only care about kcl
// files here.
2025-03-17 15:55:25 -07:00
. map ( | file | {
let options = options . clone ( ) ;
tokio ::spawn ( async move {
2025-03-19 16:43:10 -07:00
let contents = tokio ::fs ::read_to_string ( & file )
. await
. map_err ( | err | anyhow ::anyhow! ( " Failed to read file `{}`: {:?} " , file . display ( ) , err ) ) ? ;
let ( program , ces ) = crate ::Program ::parse ( & contents ) . map_err ( | err | {
let report = crate ::Report {
kcl_source : contents . to_string ( ) ,
error : err . clone ( ) ,
filename : file . to_string_lossy ( ) . to_string ( ) ,
} ;
let report = miette ::Report ::new ( report ) ;
anyhow ::anyhow! ( " {:?} " , report )
2025-03-17 15:55:25 -07:00
} ) ? ;
for ce in & ces {
if ce . severity ! = crate ::errors ::Severity ::Warning {
2025-03-19 16:43:10 -07:00
let report = crate ::Report {
kcl_source : contents . to_string ( ) ,
error : crate ::KclError ::Semantic ( ce . clone ( ) . into ( ) ) ,
filename : file . to_string_lossy ( ) . to_string ( ) ,
} ;
let report = miette ::Report ::new ( report ) ;
anyhow ::bail! ( " {:?} " , report ) ;
2025-03-17 15:55:25 -07:00
}
}
let Some ( program ) = program else {
2025-03-19 16:43:10 -07:00
anyhow ::bail! ( " Failed to parse file `{}` " , file . display ( ) ) ;
2025-03-17 15:55:25 -07:00
} ;
let recast = program . recast_with_options ( & options ) ;
2025-03-19 16:43:10 -07:00
tokio ::fs ::write ( & file , recast )
. await
. map_err ( | err | anyhow ::anyhow! ( " Failed to write file `{}`: {:?} " , file . display ( ) , err ) ) ? ;
2025-03-17 15:55:25 -07:00
2025-03-19 16:43:10 -07:00
Ok ::< ( ) , anyhow ::Error > ( ( ) )
2025-03-17 15:55:25 -07:00
} )
} )
. collect ::< Vec < _ > > ( ) ;
// Join all futures and await their completion
let results = futures ::future ::join_all ( futures ) . await ;
// Check if any of the futures failed.
let mut errors = Vec ::new ( ) ;
for result in results {
2025-03-19 16:43:10 -07:00
if let Err ( err ) = result ? {
2025-03-17 15:55:25 -07:00
errors . push ( err ) ;
}
}
if ! errors . is_empty ( ) {
2025-03-19 16:43:10 -07:00
anyhow ::bail! ( " Failed to recast some files: {:?} " , errors ) ;
2025-03-17 15:55:25 -07:00
}
Ok ( ( ) )
}
2024-09-07 12:51:35 -04:00
#[ cfg(test) ]
mod tests {
use pretty_assertions ::assert_eq ;
2024-11-27 15:46:58 +13:00
use super ::* ;
2025-02-11 13:52:46 +13:00
use crate ::{ parsing ::ast ::types ::FormatOptions , ModuleId } ;
2024-09-07 12:51:35 -04:00
2025-02-11 16:58:48 -05:00
#[ test ]
fn test_recast_annotations_without_body_items ( ) {
let input = r #" @settings(defaultLengthUnit = in)
" #;
let program = crate ::parsing ::top_level_parse ( input ) . unwrap ( ) ;
let output = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! ( output , input ) ;
}
#[ test ]
fn test_recast_annotations_in_function_body ( ) {
let input = r #" fn myFunc() {
@ meta ( yes = true )
2025-03-20 16:23:20 +13:00
2025-02-11 16:58:48 -05:00
x = 2
}
" #;
let program = crate ::parsing ::top_level_parse ( input ) . unwrap ( ) ;
let output = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! ( output , input ) ;
}
#[ test ]
fn test_recast_annotations_in_function_body_without_items ( ) {
let input = r #" fn myFunc() {
@ meta ( yes = true )
}
" #;
let program = crate ::parsing ::top_level_parse ( input ) . unwrap ( ) ;
let output = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! ( output , input ) ;
}
2025-03-20 16:23:20 +13:00
#[ test ]
fn recast_annotations_with_comments ( ) {
let input = r #" // Start comment
// Comment on attr
@ settings ( defaultLengthUnit = in )
// Comment on item
foo = 42
// Comment on another item
@ ( impl = kcl )
bar = 0
" #;
let program = crate ::parsing ::top_level_parse ( input ) . unwrap ( ) ;
let output = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! ( output , input ) ;
}
2025-04-11 15:19:52 +12:00
#[ test ]
fn recast_annotations_with_block_comment ( ) {
let input = r #" /* Start comment
sdfsdfsdfs * /
@ settings ( defaultLengthUnit = in )
foo = 42
" #;
let program = crate ::parsing ::top_level_parse ( input ) . unwrap ( ) ;
let output = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! ( output , input ) ;
}
2024-09-30 15:40:50 -05:00
#[ test ]
fn test_recast_if_else_if_same ( ) {
2024-10-02 14:19:40 -05:00
let input = r #" b = if false {
2024-09-30 15:40:50 -05:00
3
} else if true {
4
} else {
5
}
" #;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( input ) . unwrap ( ) ;
2024-09-30 15:40:50 -05:00
let output = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! ( output , input ) ;
}
#[ test ]
fn test_recast_if_same ( ) {
2024-10-02 14:19:40 -05:00
let input = r #" b = if false {
2024-09-30 15:40:50 -05:00
3
} else {
5
}
" #;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( input ) . unwrap ( ) ;
2024-09-30 15:40:50 -05:00
let output = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! ( output , input ) ;
}
2024-10-17 00:48:33 -04:00
#[ test ]
fn test_recast_import ( ) {
let input = r #" import a from " a . kcl "
import a as aaa from " a.kcl "
import a , b from " a.kcl "
import a as aaa , b from " a.kcl "
import a , b as bbb from " a.kcl "
import a as aaa , b as bbb from " a.kcl "
2024-12-07 07:16:04 +13:00
import " a_b.kcl "
import " a-b.kcl " as b
import * from " a.kcl "
export import a as aaa from " a.kcl "
export import a , b from " a.kcl "
export import a as aaa , b from " a.kcl "
export import a , b as bbb from " a.kcl "
2024-10-17 00:48:33 -04:00
" #;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( input ) . unwrap ( ) ;
2024-10-17 00:48:33 -04:00
let output = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! ( output , input ) ;
}
#[ test ]
fn test_recast_import_as_same_name ( ) {
let input = r #" import a as a from " a . kcl "
" #;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( input ) . unwrap ( ) ;
2024-10-17 00:48:33 -04:00
let output = program . recast ( & Default ::default ( ) , 0 ) ;
let expected = r #" import a from " a . kcl "
" #;
assert_eq! ( output , expected ) ;
}
#[ test ]
fn test_recast_export_fn ( ) {
2024-11-27 15:46:58 +13:00
let input = r #" export fn a() {
2024-10-17 00:48:33 -04:00
return 0
}
" #;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( input ) . unwrap ( ) ;
2024-10-17 00:48:33 -04:00
let output = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! ( output , input ) ;
}
2024-09-07 12:51:35 -04:00
#[ test ]
fn test_recast_bug_fn_in_fn ( ) {
let some_program_string = r #" // Start point (top left)
2024-10-02 14:19:40 -05:00
zoo_x = - 20
zoo_y = 7
2024-09-07 12:51:35 -04:00
// Scale
2024-10-02 14:19:40 -05:00
s = 1 // s = 1 -> height of Z is 13.4mm
2024-09-07 12:51:35 -04:00
// Depth
2024-10-02 14:19:40 -05:00
d = 1
2024-09-07 12:51:35 -04:00
2024-11-27 15:46:58 +13:00
fn rect ( x , y , w , h ) {
2025-03-21 22:39:12 +13:00
startSketchOn ( XY )
2024-09-07 12:51:35 -04:00
| > startProfileAt ( [ x , y ] , % )
2025-03-07 22:07:16 -06:00
| > xLine ( length = w )
| > yLine ( length = h )
| > xLine ( length = - w )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > close ( )
2024-09-07 12:51:35 -04:00
| > extrude ( d , % )
}
2024-11-27 15:46:58 +13:00
fn quad ( x1 , y1 , x2 , y2 , x3 , y3 , x4 , y4 ) {
2025-03-21 22:39:12 +13:00
startSketchOn ( XY )
2024-09-07 12:51:35 -04:00
| > startProfileAt ( [ x1 , y1 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( endAbsolute = [ x2 , y2 ] )
| > line ( endAbsolute = [ x3 , y3 ] )
| > line ( endAbsolute = [ x4 , y4 ] )
| > close ( )
2024-09-07 12:51:35 -04:00
| > extrude ( d , % )
}
2024-11-27 15:46:58 +13:00
fn crosshair ( x , y ) {
2025-03-21 22:39:12 +13:00
startSketchOn ( XY )
2024-09-07 12:51:35 -04:00
| > startProfileAt ( [ x , y ] , % )
2025-03-07 22:07:16 -06:00
| > yLine ( length = 1 )
| > yLine ( length = - 2 )
| > yLine ( length = 1 )
| > xLine ( length = 1 )
| > xLine ( length = - 2 )
2024-09-07 12:51:35 -04:00
}
2024-11-27 15:46:58 +13:00
fn z ( z_x , z_y ) {
2024-10-02 14:19:40 -05:00
z_end_w = s * 8.4
z_end_h = s * 3
z_corner = s * 2
z_w = z_end_w + 2 * z_corner
z_h = z_w * 1.08130081300813
2024-09-07 12:51:35 -04:00
rect ( z_x , z_y , z_end_w , - z_end_h )
rect ( z_x + z_w , z_y , - z_corner , - z_corner )
rect ( z_x + z_w , z_y - z_h , - z_end_w , z_end_h )
rect ( z_x , z_y - z_h , z_corner , z_corner )
quad ( z_x , z_y - z_h + z_corner , z_x + z_w - z_corner , z_y , z_x + z_w , z_y - z_corner , z_x + z_corner , z_y - z_h )
}
2024-11-27 15:46:58 +13:00
fn o ( c_x , c_y ) {
2024-09-07 12:51:35 -04:00
// Outer and inner radii
2024-10-02 14:19:40 -05:00
o_r = s * 6.95
i_r = 0.5652173913043478 * o_r
2024-09-07 12:51:35 -04:00
// Angle offset for diagonal break
2024-10-02 14:19:40 -05:00
a = 7
2024-09-07 12:51:35 -04:00
// Start point for the top sketch
2024-10-02 14:19:40 -05:00
o_x1 = c_x + o_r * cos ( ( 45 + a ) / 360 * tau ( ) )
o_y1 = c_y + o_r * sin ( ( 45 + a ) / 360 * tau ( ) )
2024-09-07 12:51:35 -04:00
// Start point for the bottom sketch
2024-10-02 14:19:40 -05:00
o_x2 = c_x + o_r * cos ( ( 225 + a ) / 360 * tau ( ) )
o_y2 = c_y + o_r * sin ( ( 225 + a ) / 360 * tau ( ) )
2024-09-07 12:51:35 -04:00
2025-03-01 16:32:46 -08:00
// End point for the bottom startSketch
2024-10-02 14:19:40 -05:00
o_x3 = c_x + o_r * cos ( ( 45 - a ) / 360 * tau ( ) )
o_y3 = c_y + o_r * sin ( ( 45 - a ) / 360 * tau ( ) )
2024-09-07 12:51:35 -04:00
// Where is the center?
// crosshair(c_x, c_y)
2025-03-21 22:39:12 +13:00
startSketchOn ( XY )
2024-09-07 12:51:35 -04:00
| > startProfileAt ( [ o_x1 , o_y1 ] , % )
| > arc ( {
2024-11-25 09:21:55 +13:00
radius = o_r ,
angle_start = 45 + a ,
angle_end = 225 - a
2024-09-07 12:51:35 -04:00
} , % )
KCL: Angled line should use keyword args (#5803)
We continue migrating KCL stdlib functions to use keyword arguments. Next up is the `angledLine` family of functions (except `angledLineThatIntersects, which will be a quick follow-up).
Before vs. after:
`angledLine({angle = 90, length = 3}, %, $edge)`
=> `angledLine(angle = 90, length = 3, tag = $edge)`
`angledLineOfXLength({angle = 90, length = 3}, %, $edge)`
=> `angledLine(angle = 90, lengthX = 3, tag = $edge)`
`angledLineOfYLength({angle = 90, length = 3}, %, $edge)`
=> `angledLine(angle = 90, lengthY = 3, tag = $edge)`
`angledLineToX({angle = 90, length = 3}, %, $edge)`
=> `angledLine(angle = 90, endAbsoluteX = 3, tag = $edge)`
`angledLineToY({angle = 90, length = 3}, %, $edge)`
=> `angledLine(angle = 90, endAbsoluteY = 3, tag = $edge)`
2025-04-09 14:55:15 -05:00
| > angledLine ( angle = 45 , length = o_r - i_r )
2024-09-07 12:51:35 -04:00
| > arc ( {
2024-11-25 09:21:55 +13:00
radius = i_r ,
angle_start = 225 - a ,
angle_end = 45 + a
2024-09-07 12:51:35 -04:00
} , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > close ( )
2024-09-07 12:51:35 -04:00
| > extrude ( d , % )
2025-03-21 22:39:12 +13:00
startSketchOn ( XY )
2024-09-07 12:51:35 -04:00
| > startProfileAt ( [ o_x2 , o_y2 ] , % )
| > arc ( {
2024-11-25 09:21:55 +13:00
radius = o_r ,
angle_start = 225 + a ,
angle_end = 360 + 45 - a
2024-09-07 12:51:35 -04:00
} , % )
KCL: Angled line should use keyword args (#5803)
We continue migrating KCL stdlib functions to use keyword arguments. Next up is the `angledLine` family of functions (except `angledLineThatIntersects, which will be a quick follow-up).
Before vs. after:
`angledLine({angle = 90, length = 3}, %, $edge)`
=> `angledLine(angle = 90, length = 3, tag = $edge)`
`angledLineOfXLength({angle = 90, length = 3}, %, $edge)`
=> `angledLine(angle = 90, lengthX = 3, tag = $edge)`
`angledLineOfYLength({angle = 90, length = 3}, %, $edge)`
=> `angledLine(angle = 90, lengthY = 3, tag = $edge)`
`angledLineToX({angle = 90, length = 3}, %, $edge)`
=> `angledLine(angle = 90, endAbsoluteX = 3, tag = $edge)`
`angledLineToY({angle = 90, length = 3}, %, $edge)`
=> `angledLine(angle = 90, endAbsoluteY = 3, tag = $edge)`
2025-04-09 14:55:15 -05:00
| > angledLine ( angle = 225 , length = o_r - i_r )
2024-09-07 12:51:35 -04:00
| > arc ( {
2024-11-25 09:21:55 +13:00
radius = i_r ,
angle_start = 45 - a ,
angle_end = 225 + a - 360
2024-09-07 12:51:35 -04:00
} , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > close ( )
2024-09-07 12:51:35 -04:00
| > extrude ( d , % )
}
2024-11-27 15:46:58 +13:00
fn zoo ( x0 , y0 ) {
2024-09-07 12:51:35 -04:00
z ( x0 , y0 )
o ( x0 + s * 20 , y0 - ( s * 6.7 ) )
o ( x0 + s * 35 , y0 - ( s * 6.7 ) )
}
zoo ( zoo_x , zoo_y )
" #;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! ( recasted , some_program_string ) ;
}
#[ test ]
fn test_recast_bug_extra_parens ( ) {
let some_program_string = r #" // Ball Bearing
// A ball bearing is a type of rolling-element bearing that uses balls to maintain the separation between the bearing races. The primary purpose of a ball bearing is to reduce rotational friction and support radial and axial loads.
// Define constants like ball diameter, inside diameter, overhange length, and thickness
2024-10-02 14:19:40 -05:00
sphereDia = 0.5
insideDia = 1
thickness = 0.25
overHangLength = . 4
2024-09-07 12:51:35 -04:00
// Sketch and revolve the inside bearing piece
2025-03-21 22:39:12 +13:00
insideRevolve = startSketchOn ( XZ )
2024-09-07 12:51:35 -04:00
| > startProfileAt ( [ insideDia / 2 , 0 ] , % )
| > line ( [ 0 , thickness + sphereDia / 2 ] , % )
| > line ( [ overHangLength , 0 ] , % )
| > line ( [ 0 , - thickness ] , % )
| > line ( [ - overHangLength + thickness , 0 ] , % )
| > line ( [ 0 , - sphereDia ] , % )
| > line ( [ overHangLength - thickness , 0 ] , % )
| > line ( [ 0 , - thickness ] , % )
| > line ( [ - overHangLength , 0 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > close ( )
2025-04-03 22:44:52 +13:00
| > revolve ( { axis = Y } , % )
2024-09-07 12:51:35 -04:00
// Sketch and revolve one of the balls and duplicate it using a circular pattern. (This is currently a workaround, we have a bug with rotating on a sketch that touches the rotation axis)
2025-03-21 22:39:12 +13:00
sphere = startSketchOn ( XZ )
2024-09-07 12:51:35 -04:00
| > startProfileAt ( [
0.05 + insideDia / 2 + thickness ,
0 - 0.05
] , % )
| > line ( [ sphereDia - 0.1 , 0 ] , % )
| > arc ( {
2024-11-25 09:21:55 +13:00
angle_start = 0 ,
angle_end = - 180 ,
radius = sphereDia / 2 - 0.05
2024-09-07 12:51:35 -04:00
} , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > close ( )
2025-04-03 22:44:52 +13:00
| > revolve ( { axis = X } , % )
2025-02-11 16:06:47 -06:00
| > patternCircular3d (
2024-11-25 09:21:55 +13:00
axis = [ 0 , 0 , 1 ] ,
center = [ 0 , 0 , 0 ] ,
repetitions = 10 ,
arcDegrees = 360 ,
rotateDuplicates = true
2025-02-11 16:06:47 -06:00
)
2024-09-07 12:51:35 -04:00
// Sketch and revolve the outside bearing
2025-03-21 22:39:12 +13:00
outsideRevolve = startSketchOn ( XZ )
2024-09-07 12:51:35 -04:00
| > startProfileAt ( [
insideDia / 2 + thickness + sphereDia ,
0
] , % )
| > line ( [ 0 , sphereDia / 2 ] , % )
| > line ( [ - overHangLength + thickness , 0 ] , % )
| > line ( [ 0 , thickness ] , % )
| > line ( [ overHangLength , 0 ] , % )
| > line ( [ 0 , - 2 * thickness - sphereDia ] , % )
| > line ( [ - overHangLength , 0 ] , % )
| > line ( [ 0 , thickness ] , % )
| > line ( [ overHangLength - thickness , 0 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > close ( )
2025-04-03 22:44:52 +13:00
| > revolve ( { axis = Y } , % ) " #;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! (
recasted ,
r #" // Ball Bearing
// A ball bearing is a type of rolling-element bearing that uses balls to maintain the separation between the bearing races. The primary purpose of a ball bearing is to reduce rotational friction and support radial and axial loads.
// Define constants like ball diameter, inside diameter, overhange length, and thickness
2024-10-02 14:19:40 -05:00
sphereDia = 0.5
insideDia = 1
thickness = 0.25
overHangLength = . 4
2024-09-07 12:51:35 -04:00
// Sketch and revolve the inside bearing piece
2025-03-21 22:39:12 +13:00
insideRevolve = startSketchOn ( XZ )
2024-09-07 12:51:35 -04:00
| > startProfileAt ( [ insideDia / 2 , 0 ] , % )
| > line ( [ 0 , thickness + sphereDia / 2 ] , % )
| > line ( [ overHangLength , 0 ] , % )
| > line ( [ 0 , - thickness ] , % )
| > line ( [ - overHangLength + thickness , 0 ] , % )
| > line ( [ 0 , - sphereDia ] , % )
| > line ( [ overHangLength - thickness , 0 ] , % )
| > line ( [ 0 , - thickness ] , % )
| > line ( [ - overHangLength , 0 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > close ( )
2025-04-03 22:44:52 +13:00
| > revolve ( { axis = Y } , % )
2024-09-07 12:51:35 -04:00
// Sketch and revolve one of the balls and duplicate it using a circular pattern. (This is currently a workaround, we have a bug with rotating on a sketch that touches the rotation axis)
2025-03-21 22:39:12 +13:00
sphere = startSketchOn ( XZ )
2024-09-07 12:51:35 -04:00
| > startProfileAt ( [
0.05 + insideDia / 2 + thickness ,
0 - 0.05
] , % )
| > line ( [ sphereDia - 0.1 , 0 ] , % )
| > arc ( {
2024-11-25 09:21:55 +13:00
angle_start = 0 ,
angle_end = - 180 ,
radius = sphereDia / 2 - 0.05
2024-09-07 12:51:35 -04:00
} , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > close ( )
2025-04-03 22:44:52 +13:00
| > revolve ( { axis = X } , % )
2025-02-11 16:06:47 -06:00
| > patternCircular3d (
2024-11-25 09:21:55 +13:00
axis = [ 0 , 0 , 1 ] ,
center = [ 0 , 0 , 0 ] ,
repetitions = 10 ,
arcDegrees = 360 ,
2025-02-11 16:06:47 -06:00
rotateDuplicates = true ,
)
2024-09-07 12:51:35 -04:00
// Sketch and revolve the outside bearing
2025-03-21 22:39:12 +13:00
outsideRevolve = startSketchOn ( XZ )
2024-09-07 12:51:35 -04:00
| > startProfileAt ( [
insideDia / 2 + thickness + sphereDia ,
0
] , % )
| > line ( [ 0 , sphereDia / 2 ] , % )
| > line ( [ - overHangLength + thickness , 0 ] , % )
| > line ( [ 0 , thickness ] , % )
| > line ( [ overHangLength , 0 ] , % )
| > line ( [ 0 , - 2 * thickness - sphereDia ] , % )
| > line ( [ - overHangLength , 0 ] , % )
| > line ( [ 0 , thickness ] , % )
| > line ( [ overHangLength - thickness , 0 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > close ( )
2025-04-03 22:44:52 +13:00
| > revolve ( { axis = Y } , % )
2024-09-07 12:51:35 -04:00
" #
) ;
}
#[ test ]
fn test_recast_fn_in_object ( ) {
2024-11-25 09:21:55 +13:00
let some_program_string = r #" bing = { yo = 55 }
myNestedVar = [ { prop = callExp ( bing . yo ) } ]
2024-09-07 12:51:35 -04:00
" #;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! ( recasted , some_program_string ) ;
}
#[ test ]
fn test_recast_fn_in_array ( ) {
2024-11-25 09:21:55 +13:00
let some_program_string = r #" bing = { yo = 55 }
2024-10-02 14:19:40 -05:00
myNestedVar = [ callExp ( bing . yo ) ]
2024-10-17 02:58:04 +13:00
" #;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-10-17 02:58:04 +13:00
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! ( recasted , some_program_string ) ;
}
#[ test ]
fn test_recast_ranges ( ) {
let some_program_string = r #" foo = [0..10]
ten = 10
bar = [ 0 + 1 .. ten ]
2024-09-07 12:51:35 -04:00
" #;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! ( recasted , some_program_string ) ;
}
#[ test ]
fn test_recast_space_in_fn_call ( ) {
let some_program_string = r #" fn thing = (x) => {
return x + 1
}
thing ( 1 )
" #;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! (
recasted ,
2024-11-27 15:46:58 +13:00
r #" fn thing(x) {
2024-09-07 12:51:35 -04:00
return x + 1
}
thing ( 1 )
" #
) ;
}
2024-11-27 15:46:58 +13:00
#[ test ]
fn test_recast_typed_fn ( ) {
2025-03-17 17:57:26 +13:00
let some_program_string = r #" fn thing(x: string, y: [bool]): number {
2024-11-27 15:46:58 +13:00
return x + 1
}
" #;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-11-27 15:46:58 +13:00
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! ( recasted , some_program_string ) ;
}
2025-03-21 10:56:55 +13:00
#[ test ]
fn test_recast_typed_consts ( ) {
let some_program_string = r #" a = 42: number
export b = 3.2 : number ( ft )
c = " dsfds " : A | B | C
d = [ 1 ] : [ number ]
e = foo : [ number ; 3 ]
f = [ 1 , 2 , 3 ] : [ number ; 1 + ]
" #;
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! ( recasted , some_program_string ) ;
}
2024-09-07 12:51:35 -04:00
#[ test ]
fn test_recast_object_fn_in_array_weird_bracket ( ) {
2024-11-25 09:21:55 +13:00
let some_program_string = r #" bing = { yo = 55 }
2024-10-02 14:19:40 -05:00
myNestedVar = [
2024-09-07 12:51:35 -04:00
{
prop : line ( [ bing . yo , 21 ] , sketch001 )
}
]
" #;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! (
recasted ,
2024-11-25 09:21:55 +13:00
r #" bing = { yo = 55 }
2024-10-02 14:19:40 -05:00
myNestedVar = [
2024-11-25 09:21:55 +13:00
{
prop = line ( [ bing . yo , 21 ] , sketch001 )
}
2024-09-07 12:51:35 -04:00
]
" #
) ;
}
#[ test ]
fn test_recast_empty_file ( ) {
let some_program_string = r # ""# ;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
// Its VERY important this comes back with zero new lines.
assert_eq! ( recasted , r # ""# ) ;
}
#[ test ]
fn test_recast_empty_file_new_line ( ) {
let some_program_string = r #"
" #;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
// Its VERY important this comes back with zero new lines.
assert_eq! ( recasted , r # ""# ) ;
}
#[ test ]
fn test_recast_shebang ( ) {
let some_program_string = r #" #!/usr/local/env zoo kcl
2025-03-21 22:39:12 +13:00
part001 = startSketchOn ( XY )
2024-09-07 12:51:35 -04:00
| > startProfileAt ( [ - 10 , - 10 ] , % )
| > line ( [ 20 , 0 ] , % )
| > line ( [ 0 , 20 ] , % )
| > line ( [ - 20 , 0 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > close ( )
2024-09-07 12:51:35 -04:00
" #;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! (
recasted ,
r #" #!/usr/local/env zoo kcl
2025-03-21 22:39:12 +13:00
part001 = startSketchOn ( XY )
2024-09-07 12:51:35 -04:00
| > startProfileAt ( [ - 10 , - 10 ] , % )
| > line ( [ 20 , 0 ] , % )
| > line ( [ 0 , 20 ] , % )
| > line ( [ - 20 , 0 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > close ( )
2024-09-07 12:51:35 -04:00
" #
) ;
}
#[ test ]
fn test_recast_shebang_new_lines ( ) {
let some_program_string = r #" #!/usr/local/env zoo kcl
2025-03-21 22:39:12 +13:00
part001 = startSketchOn ( XY )
2024-09-07 12:51:35 -04:00
| > startProfileAt ( [ - 10 , - 10 ] , % )
| > line ( [ 20 , 0 ] , % )
| > line ( [ 0 , 20 ] , % )
| > line ( [ - 20 , 0 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > close ( )
2024-09-07 12:51:35 -04:00
" #;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! (
recasted ,
r #" #!/usr/local/env zoo kcl
2025-03-21 22:39:12 +13:00
part001 = startSketchOn ( XY )
2024-09-07 12:51:35 -04:00
| > startProfileAt ( [ - 10 , - 10 ] , % )
| > line ( [ 20 , 0 ] , % )
| > line ( [ 0 , 20 ] , % )
| > line ( [ - 20 , 0 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > close ( )
2024-09-07 12:51:35 -04:00
" #
) ;
}
#[ test ]
fn test_recast_shebang_with_comments ( ) {
let some_program_string = r #" #!/usr/local/env zoo kcl
// Yo yo my comments.
2025-03-21 22:39:12 +13:00
part001 = startSketchOn ( XY )
2024-09-07 12:51:35 -04:00
| > startProfileAt ( [ - 10 , - 10 ] , % )
| > line ( [ 20 , 0 ] , % )
| > line ( [ 0 , 20 ] , % )
| > line ( [ - 20 , 0 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > close ( )
2024-09-07 12:51:35 -04:00
" #;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! (
recasted ,
r #" #!/usr/local/env zoo kcl
// Yo yo my comments.
2025-03-21 22:39:12 +13:00
part001 = startSketchOn ( XY )
2024-09-07 12:51:35 -04:00
| > startProfileAt ( [ - 10 , - 10 ] , % )
| > line ( [ 20 , 0 ] , % )
| > line ( [ 0 , 20 ] , % )
| > line ( [ - 20 , 0 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > close ( )
2024-09-07 12:51:35 -04:00
" #
) ;
}
2025-02-11 16:58:48 -05:00
#[ test ]
fn test_recast_empty_function_body_with_comments ( ) {
let input = r #" fn myFunc() {
// Yo yo my comments.
}
" #;
let program = crate ::parsing ::top_level_parse ( input ) . unwrap ( ) ;
let output = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! ( output , input ) ;
}
2024-09-07 12:51:35 -04:00
#[ test ]
fn test_recast_large_file ( ) {
2024-12-17 15:23:00 +13:00
let some_program_string = r #" @settings(units=mm)
// define nts
2024-10-02 14:19:40 -05:00
radius = 6.0
width = 144.0
length = 83.0
depth = 45.0
thk = 5
hole_diam = 5
2024-09-07 12:51:35 -04:00
// define a rectangular shape func
fn rectShape = ( pos , w , l ) = > {
2024-10-02 14:19:40 -05:00
rr = startSketchOn ( ' xy ' )
2024-09-07 12:51:35 -04:00
| > startProfileAt ( [ pos [ 0 ] - ( w / 2 ) , pos [ 1 ] - ( l / 2 ) ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( endAbsolute = [ pos [ 0 ] + w / 2 , pos [ 1 ] - ( l / 2 ) ] , tag = $edge1 )
| > line ( endAbsolute = [ pos [ 0 ] + w / 2 , pos [ 1 ] + l / 2 ] , tag = $edge2 )
| > line ( endAbsolute = [ pos [ 0 ] - ( w / 2 ) , pos [ 1 ] + l / 2 ] , tag = $edge3 )
| > close ( $edge4 )
2024-09-07 12:51:35 -04:00
return rr
}
// build the body of the focusrite scarlett solo gen 4
// only used for visualization
2024-10-02 14:19:40 -05:00
scarlett_body = rectShape ( [ 0 , 0 ] , width , length )
2024-09-07 12:51:35 -04:00
| > extrude ( depth , % )
2025-02-21 14:41:25 -06:00
| > fillet (
2024-11-25 09:21:55 +13:00
radius = radius ,
tags = [
2024-09-07 12:51:35 -04:00
edge2 ,
edge4 ,
getOppositeEdge ( edge2 ) ,
getOppositeEdge ( edge4 )
]
2025-02-21 14:41:25 -06:00
)
2024-09-07 12:51:35 -04:00
// build the bracket sketch around the body
fn bracketSketch = ( w , d , t ) = > {
2024-10-02 14:19:40 -05:00
s = startSketchOn ( {
2024-09-07 12:51:35 -04:00
plane : {
2024-11-25 09:21:55 +13:00
origin : { x = 0 , y = length / 2 + thk , z = 0 } ,
2025-04-03 22:44:52 +13:00
x_axis = { x = 1 , y = 0 , z = 0 } ,
y_axis = { x = 0 , y = 0 , z = 1 } ,
z_axis = { x = 0 , y = 1 , z = 0 }
2024-09-07 12:51:35 -04:00
}
} )
| > startProfileAt ( [ - w / 2 - t , d + t ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( endAbsolute = [ - w / 2 - t , - t ] , tag = $edge1 )
| > line ( endAbsolute = [ w / 2 + t , - t ] , tag = $edge2 )
| > line ( endAbsolute = [ w / 2 + t , d + t ] , tag = $edge3 )
| > line ( endAbsolute = [ w / 2 , d + t ] , tag = $edge4 )
| > line ( endAbsolute = [ w / 2 , 0 ] , tag = $edge5 )
| > line ( endAbsolute = [ - w / 2 , 0 ] , tag = $edge6 )
| > line ( endAbsolute = [ - w / 2 , d + t ] , tag = $edge7 )
| > close ( $edge8 )
2024-09-07 12:51:35 -04:00
return s
}
// build the body of the bracket
2024-10-02 14:19:40 -05:00
bracket_body = bracketSketch ( width , depth , thk )
2024-09-07 12:51:35 -04:00
| > extrude ( length + 10 , % )
2025-02-21 14:41:25 -06:00
| > fillet (
2024-11-25 09:21:55 +13:00
radius = radius ,
2025-02-21 14:41:25 -06:00
tags = [
2024-09-07 12:51:35 -04:00
getNextAdjacentEdge ( edge7 ) ,
getNextAdjacentEdge ( edge2 ) ,
getNextAdjacentEdge ( edge3 ) ,
getNextAdjacentEdge ( edge6 )
]
2025-02-21 14:41:25 -06:00
)
2024-09-07 12:51:35 -04:00
// build the tabs of the mounting bracket (right side)
2024-10-02 14:19:40 -05:00
tabs_r = startSketchOn ( {
2024-09-07 12:51:35 -04:00
plane : {
2024-11-25 09:21:55 +13:00
origin : { x = 0 , y = 0 , z = depth + thk } ,
2025-04-03 22:44:52 +13:00
x_axis = { x = 1 , y = 0 , z = 0 } ,
y_axis = { x = 0 , y = 1 , z = 0 } ,
z_axis = { x = 0 , y = 0 , z = 1 }
2024-09-07 12:51:35 -04:00
}
} )
| > startProfileAt ( [ width / 2 + thk , length / 2 + thk ] , % )
| > line ( [ 10 , - 5 ] , % )
| > line ( [ 0 , - 10 ] , % )
| > line ( [ - 10 , - 5 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > close ( )
2025-02-28 17:40:01 -08:00
| > hole ( circle (
2024-11-25 09:21:55 +13:00
center = [
2024-09-23 22:42:51 +10:00
width / 2 + thk + hole_diam ,
length / 2 - hole_diam
] ,
2024-11-25 09:21:55 +13:00
radius = hole_diam / 2
2025-02-28 17:40:01 -08:00
) , % )
2024-09-07 12:51:35 -04:00
| > extrude ( - thk , % )
2025-02-11 16:06:47 -06:00
| > patternLinear3d (
2024-11-25 09:21:55 +13:00
axis = [ 0 , - 1 , 0 ] ,
repetitions = 1 ,
distance = length - 10
2025-02-11 16:06:47 -06:00
)
2024-09-07 12:51:35 -04:00
// build the tabs of the mounting bracket (left side)
2024-10-02 14:19:40 -05:00
tabs_l = startSketchOn ( {
2024-09-07 12:51:35 -04:00
plane : {
2024-11-25 09:21:55 +13:00
origin = { x = 0 , y = 0 , z = depth + thk } ,
x_axis = { x = 1 , y = 0 , z = 0 } ,
y_axis = { x = 0 , y = 1 , z = 0 } ,
z_axis = { x = 0 , y = 0 , z = 1 }
2024-09-07 12:51:35 -04:00
}
} )
| > startProfileAt ( [ - width / 2 - thk , length / 2 + thk ] , % )
| > line ( [ - 10 , - 5 ] , % )
| > line ( [ 0 , - 10 ] , % )
| > line ( [ 10 , - 5 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > close ( )
2025-02-28 17:40:01 -08:00
| > hole ( circle (
2024-11-25 09:21:55 +13:00
center = [
2024-09-23 22:42:51 +10:00
- width / 2 - thk - hole_diam ,
length / 2 - hole_diam
] ,
2024-11-25 09:21:55 +13:00
radius = hole_diam / 2
2025-02-28 17:40:01 -08:00
) , % )
2024-09-07 12:51:35 -04:00
| > extrude ( - thk , % )
2025-03-09 03:33:45 +13:00
| > patternLinear3d ( axis = [ 0 , - 1 , 0 ] , repetitions = 1 , distance = length - 10 ft )
2024-09-07 12:51:35 -04:00
" #;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
// Its VERY important this comes back with zero new lines.
assert_eq! (
recasted ,
2024-12-17 15:23:00 +13:00
r #" @settings(units = mm)
2025-03-20 16:23:20 +13:00
2024-12-17 15:23:00 +13:00
// define nts
2024-10-02 14:19:40 -05:00
radius = 6.0
width = 144.0
length = 83.0
depth = 45.0
thk = 5
hole_diam = 5
2024-09-07 12:51:35 -04:00
// define a rectangular shape func
2024-11-27 15:46:58 +13:00
fn rectShape ( pos , w , l ) {
2025-04-14 20:37:45 +12:00
rr = startSketchOn ( XY )
2024-09-07 12:51:35 -04:00
| > startProfileAt ( [ pos [ 0 ] - ( w / 2 ) , pos [ 1 ] - ( l / 2 ) ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( endAbsolute = [ pos [ 0 ] + w / 2 , pos [ 1 ] - ( l / 2 ) ] , tag = $edge1 )
| > line ( endAbsolute = [ pos [ 0 ] + w / 2 , pos [ 1 ] + l / 2 ] , tag = $edge2 )
| > line ( endAbsolute = [ pos [ 0 ] - ( w / 2 ) , pos [ 1 ] + l / 2 ] , tag = $edge3 )
| > close ( $edge4 )
2024-09-07 12:51:35 -04:00
return rr
}
// build the body of the focusrite scarlett solo gen 4
// only used for visualization
2024-10-02 14:19:40 -05:00
scarlett_body = rectShape ( [ 0 , 0 ] , width , length )
2024-09-07 12:51:35 -04:00
| > extrude ( depth , % )
2025-02-21 14:41:25 -06:00
| > fillet (
2024-11-25 09:21:55 +13:00
radius = radius ,
tags = [
2024-09-07 12:51:35 -04:00
edge2 ,
edge4 ,
getOppositeEdge ( edge2 ) ,
getOppositeEdge ( edge4 )
2025-02-21 14:41:25 -06:00
] ,
)
2024-09-07 12:51:35 -04:00
// build the bracket sketch around the body
2024-11-27 15:46:58 +13:00
fn bracketSketch ( w , d , t ) {
2024-10-02 14:19:40 -05:00
s = startSketchOn ( {
2024-11-25 09:21:55 +13:00
plane = {
origin = { x = 0 , y = length / 2 + thk , z = 0 } ,
x_axis = { x = 1 , y = 0 , z = 0 } ,
y_axis = { x = 0 , y = 0 , z = 1 } ,
z_axis = { x = 0 , y = 1 , z = 0 }
2024-09-07 12:51:35 -04:00
}
} )
| > startProfileAt ( [ - w / 2 - t , d + t ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( endAbsolute = [ - w / 2 - t , - t ] , tag = $edge1 )
| > line ( endAbsolute = [ w / 2 + t , - t ] , tag = $edge2 )
| > line ( endAbsolute = [ w / 2 + t , d + t ] , tag = $edge3 )
| > line ( endAbsolute = [ w / 2 , d + t ] , tag = $edge4 )
| > line ( endAbsolute = [ w / 2 , 0 ] , tag = $edge5 )
| > line ( endAbsolute = [ - w / 2 , 0 ] , tag = $edge6 )
| > line ( endAbsolute = [ - w / 2 , d + t ] , tag = $edge7 )
| > close ( $edge8 )
2024-09-07 12:51:35 -04:00
return s
}
// build the body of the bracket
2024-10-02 14:19:40 -05:00
bracket_body = bracketSketch ( width , depth , thk )
2024-09-07 12:51:35 -04:00
| > extrude ( length + 10 , % )
2025-02-21 14:41:25 -06:00
| > fillet (
2024-11-25 09:21:55 +13:00
radius = radius ,
tags = [
2024-09-07 12:51:35 -04:00
getNextAdjacentEdge ( edge7 ) ,
getNextAdjacentEdge ( edge2 ) ,
getNextAdjacentEdge ( edge3 ) ,
getNextAdjacentEdge ( edge6 )
2025-02-21 14:41:25 -06:00
] ,
)
2024-09-07 12:51:35 -04:00
// build the tabs of the mounting bracket (right side)
2024-10-02 14:19:40 -05:00
tabs_r = startSketchOn ( {
2024-11-25 09:21:55 +13:00
plane = {
origin = { x = 0 , y = 0 , z = depth + thk } ,
x_axis = { x = 1 , y = 0 , z = 0 } ,
y_axis = { x = 0 , y = 1 , z = 0 } ,
z_axis = { x = 0 , y = 0 , z = 1 }
2024-09-07 12:51:35 -04:00
}
} )
| > startProfileAt ( [ width / 2 + thk , length / 2 + thk ] , % )
| > line ( [ 10 , - 5 ] , % )
| > line ( [ 0 , - 10 ] , % )
| > line ( [ - 10 , - 5 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > close ( )
2025-02-28 17:40:01 -08:00
| > hole ( circle (
2024-11-25 09:21:55 +13:00
center = [
2024-09-23 22:42:51 +10:00
width / 2 + thk + hole_diam ,
length / 2 - hole_diam
] ,
2025-02-28 17:40:01 -08:00
radius = hole_diam / 2 ,
) , % )
2024-09-07 12:51:35 -04:00
| > extrude ( - thk , % )
2025-02-11 16:06:47 -06:00
| > patternLinear3d ( axis = [ 0 , - 1 , 0 ] , repetitions = 1 , distance = length - 10 )
2024-09-07 12:51:35 -04:00
// build the tabs of the mounting bracket (left side)
2024-10-02 14:19:40 -05:00
tabs_l = startSketchOn ( {
2024-11-25 09:21:55 +13:00
plane = {
origin = { x = 0 , y = 0 , z = depth + thk } ,
x_axis = { x = 1 , y = 0 , z = 0 } ,
y_axis = { x = 0 , y = 1 , z = 0 } ,
z_axis = { x = 0 , y = 0 , z = 1 }
2024-09-07 12:51:35 -04:00
}
} )
| > startProfileAt ( [ - width / 2 - thk , length / 2 + thk ] , % )
| > line ( [ - 10 , - 5 ] , % )
| > line ( [ 0 , - 10 ] , % )
| > line ( [ 10 , - 5 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > close ( )
2025-02-28 17:40:01 -08:00
| > hole ( circle (
2024-11-25 09:21:55 +13:00
center = [
2024-09-23 22:42:51 +10:00
- width / 2 - thk - hole_diam ,
length / 2 - hole_diam
] ,
2025-02-28 17:40:01 -08:00
radius = hole_diam / 2 ,
) , % )
2024-09-07 12:51:35 -04:00
| > extrude ( - thk , % )
2025-03-09 03:33:45 +13:00
| > patternLinear3d ( axis = [ 0 , - 1 , 0 ] , repetitions = 1 , distance = length - 10 ft )
2024-09-07 12:51:35 -04:00
" #
) ;
}
#[ test ]
fn test_recast_nested_var_declaration_in_fn_body ( ) {
let some_program_string = r #" fn cube = (pos, scale) => {
2025-03-21 22:39:12 +13:00
sg = startSketchOn ( XY )
2024-09-07 12:51:35 -04:00
| > startProfileAt ( pos , % )
| > line ( [ 0 , scale ] , % )
| > line ( [ scale , 0 ] , % )
| > line ( [ 0 , - scale ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > close ( )
2024-09-07 12:51:35 -04:00
| > extrude ( scale , % )
} " #;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! (
recasted ,
2024-11-27 15:46:58 +13:00
r #" fn cube(pos, scale) {
2025-03-21 22:39:12 +13:00
sg = startSketchOn ( XY )
2024-09-07 12:51:35 -04:00
| > startProfileAt ( pos , % )
| > line ( [ 0 , scale ] , % )
| > line ( [ scale , 0 ] , % )
| > line ( [ 0 , - scale ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > close ( )
2024-09-07 12:51:35 -04:00
| > extrude ( scale , % )
}
" #
) ;
}
2024-12-11 21:26:42 +13:00
#[ test ]
fn test_as ( ) {
let some_program_string = r #" fn cube(pos, scale) {
x = dfsfs + dfsfsd as y
2025-03-21 22:39:12 +13:00
sg = startSketchOn ( XY )
2024-12-11 21:26:42 +13:00
| > startProfileAt ( pos , % ) as foo
| > line ( [ 0 , scale ] , % )
| > line ( [ scale , 0 ] , % ) as bar
| > line ( [ 0 as baz , - scale ] as qux , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > close ( )
2024-12-11 21:26:42 +13:00
| > extrude ( scale , % )
}
cube ( 0 , 0 ) as cub
" #;
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! ( recasted , some_program_string , ) ;
}
2024-09-07 12:51:35 -04:00
#[ test ]
fn test_recast_with_bad_indentation ( ) {
2025-03-21 22:39:12 +13:00
let some_program_string = r #" part001 = startSketchOn(XY)
2024-09-07 12:51:35 -04:00
| > startProfileAt ( [ 0.0 , 5.0 ] , % )
| > line ( [ 0.4900857016 , - 0.0240763666 ] , % )
| > line ( [ 0.6804562304 , 0.9087880491 ] , % ) " #;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! (
recasted ,
2025-03-21 22:39:12 +13:00
r #" part001 = startSketchOn(XY)
2024-09-07 12:51:35 -04:00
| > startProfileAt ( [ 0.0 , 5.0 ] , % )
| > line ( [ 0.4900857016 , - 0.0240763666 ] , % )
| > line ( [ 0.6804562304 , 0.9087880491 ] , % )
" #
) ;
}
#[ test ]
fn test_recast_with_bad_indentation_and_inline_comment ( ) {
2025-03-21 22:39:12 +13:00
let some_program_string = r #" part001 = startSketchOn(XY)
2024-09-07 12:51:35 -04:00
| > startProfileAt ( [ 0.0 , 5.0 ] , % )
| > line ( [ 0.4900857016 , - 0.0240763666 ] , % ) // hello world
| > line ( [ 0.6804562304 , 0.9087880491 ] , % ) " #;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! (
recasted ,
2025-03-21 22:39:12 +13:00
r #" part001 = startSketchOn(XY)
2024-09-07 12:51:35 -04:00
| > startProfileAt ( [ 0.0 , 5.0 ] , % )
| > line ( [ 0.4900857016 , - 0.0240763666 ] , % ) // hello world
| > line ( [ 0.6804562304 , 0.9087880491 ] , % )
" #
) ;
}
#[ test ]
fn test_recast_with_bad_indentation_and_line_comment ( ) {
2025-03-21 22:39:12 +13:00
let some_program_string = r #" part001 = startSketchOn(XY)
2024-09-07 12:51:35 -04:00
| > startProfileAt ( [ 0.0 , 5.0 ] , % )
| > line ( [ 0.4900857016 , - 0.0240763666 ] , % )
// hello world
| > line ( [ 0.6804562304 , 0.9087880491 ] , % ) " #;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! (
recasted ,
2025-03-21 22:39:12 +13:00
r #" part001 = startSketchOn(XY)
2024-09-07 12:51:35 -04:00
| > startProfileAt ( [ 0.0 , 5.0 ] , % )
| > line ( [ 0.4900857016 , - 0.0240763666 ] , % )
// hello world
| > line ( [ 0.6804562304 , 0.9087880491 ] , % )
" #
) ;
}
#[ test ]
fn test_recast_comment_in_a_fn_block ( ) {
let some_program_string = r #" fn myFn = () => {
// this is a comment
2024-11-25 09:21:55 +13:00
yo = { a = { b = { c = ' 123 ' } } } /* block
2024-09-07 12:51:35 -04:00
comment * /
2024-10-02 14:19:40 -05:00
key = 'c'
2024-09-07 12:51:35 -04:00
// this is also a comment
return things
} " #;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! (
recasted ,
2024-11-27 15:46:58 +13:00
r #" fn myFn() {
2024-09-07 12:51:35 -04:00
// this is a comment
2024-11-25 09:21:55 +13:00
yo = { a = { b = { c = ' 123 ' } } } /* block
2024-09-07 12:51:35 -04:00
comment * /
2024-10-02 14:19:40 -05:00
key = 'c'
2024-09-07 12:51:35 -04:00
// this is also a comment
return things
}
" #
) ;
}
#[ test ]
fn test_recast_comment_under_variable ( ) {
2024-10-02 14:19:40 -05:00
let some_program_string = r #" key = 'c'
2024-09-07 12:51:35 -04:00
// this is also a comment
2024-10-02 14:19:40 -05:00
thing = ' foo '
2024-09-07 12:51:35 -04:00
" #;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! (
recasted ,
2024-10-02 14:19:40 -05:00
r #" key = 'c'
2024-09-07 12:51:35 -04:00
// this is also a comment
2024-10-02 14:19:40 -05:00
thing = ' foo '
2024-09-07 12:51:35 -04:00
" #
) ;
}
#[ test ]
fn test_recast_multiline_comment_start_file ( ) {
let some_program_string = r #" // hello world
// I am a comment
2024-10-02 14:19:40 -05:00
key = 'c'
2024-09-07 12:51:35 -04:00
// this is also a comment
// hello
2024-10-02 14:19:40 -05:00
thing = ' foo '
2024-09-07 12:51:35 -04:00
" #;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! (
recasted ,
r #" // hello world
// I am a comment
2024-10-02 14:19:40 -05:00
key = 'c'
2024-09-07 12:51:35 -04:00
// this is also a comment
// hello
2024-10-02 14:19:40 -05:00
thing = ' foo '
2024-09-07 12:51:35 -04:00
" #
) ;
}
#[ test ]
fn test_recast_empty_comment ( ) {
let some_program_string = r #" // hello world
//
// I am a comment
2024-10-02 14:19:40 -05:00
key = 'c'
2024-09-07 12:51:35 -04:00
//
// I am a comment
2024-10-02 14:19:40 -05:00
thing = 'c'
2024-09-07 12:51:35 -04:00
2024-10-02 14:19:40 -05:00
foo = ' bar ' //
2024-09-07 12:51:35 -04:00
" #;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! (
recasted ,
r #" // hello world
//
// I am a comment
2024-10-02 14:19:40 -05:00
key = 'c'
2024-09-07 12:51:35 -04:00
//
// I am a comment
2024-10-02 14:19:40 -05:00
thing = 'c'
2024-09-07 12:51:35 -04:00
2024-10-02 14:19:40 -05:00
foo = ' bar ' //
2024-09-07 12:51:35 -04:00
" #
) ;
}
#[ test ]
fn test_recast_multiline_comment_under_variable ( ) {
2024-10-02 14:19:40 -05:00
let some_program_string = r #" key = 'c'
2024-09-07 12:51:35 -04:00
// this is also a comment
// hello
2024-10-02 14:19:40 -05:00
thing = ' foo '
2024-09-07 12:51:35 -04:00
" #;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! (
recasted ,
2024-10-02 14:19:40 -05:00
r #" key = 'c'
2024-09-07 12:51:35 -04:00
// this is also a comment
// hello
2024-10-02 14:19:40 -05:00
thing = ' foo '
2024-09-07 12:51:35 -04:00
" #
) ;
}
2025-03-21 20:56:04 -04:00
#[ test ]
fn test_recast_only_line_comments ( ) {
let code = r #" // comment at start
" #;
let program = crate ::parsing ::top_level_parse ( code ) . unwrap ( ) ;
assert_eq! ( program . recast ( & Default ::default ( ) , 0 ) , code ) ;
}
2024-09-07 12:51:35 -04:00
#[ test ]
fn test_recast_comment_at_start ( ) {
let test_program = r #"
/* comment at start */
2025-03-01 16:32:46 -08:00
mySk1 = startSketchOn ( XY )
| > startProfileAt ( [ 0 , 0 ] , % ) " #;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( test_program ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! (
recasted ,
r #" /* comment at start */
2025-03-01 16:32:46 -08:00
mySk1 = startSketchOn ( XY )
| > startProfileAt ( [ 0 , 0 ] , % )
2024-09-07 12:51:35 -04:00
" #
) ;
}
#[ test ]
fn test_recast_lots_of_comments ( ) {
let some_program_string = r #" // comment at start
2025-03-21 22:39:12 +13:00
mySk1 = startSketchOn ( XY )
2024-09-07 12:51:35 -04:00
| > startProfileAt ( [ 0 , 0 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( endAbsolute = [ 1 , 1 ] )
2024-09-07 12:51:35 -04:00
// comment here
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( endAbsolute = [ 0 , 1 ] , tag = $myTag )
| > line ( endAbsolute = [ 1 , 1 ] )
2024-09-07 12:51:35 -04:00
/* and
here
* /
// a comment between pipe expression statements
| > rx ( 90 , % )
// and another with just white space between others below
| > ry ( 45 , % )
| > rx ( 45 , % )
// one more for good measure"#;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! (
recasted ,
r #" // comment at start
2025-03-21 22:39:12 +13:00
mySk1 = startSketchOn ( XY )
2024-09-07 12:51:35 -04:00
| > startProfileAt ( [ 0 , 0 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( endAbsolute = [ 1 , 1 ] )
2024-09-07 12:51:35 -04:00
// comment here
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( endAbsolute = [ 0 , 1 ] , tag = $myTag )
| > line ( endAbsolute = [ 1 , 1 ] )
2024-09-07 12:51:35 -04:00
/* and
here * /
// a comment between pipe expression statements
| > rx ( 90 , % )
// and another with just white space between others below
| > ry ( 45 , % )
| > rx ( 45 , % )
// one more for good measure
" #
) ;
}
#[ test ]
fn test_recast_multiline_object ( ) {
2025-04-14 21:06:55 -05:00
let some_program_string = r #" x = {
a = 1000000000 ,
b = 2000000000 ,
c = 3000000000 ,
d = 4000000000 ,
e = 5000000000
} " #;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! ( recasted . trim ( ) , some_program_string ) ;
}
#[ test ]
fn test_recast_first_level_object ( ) {
2024-10-02 14:19:40 -05:00
let some_program_string = r #" three = 3
2024-09-07 12:51:35 -04:00
2024-10-02 14:19:40 -05:00
yo = {
2024-11-25 09:21:55 +13:00
aStr = ' str ' ,
anum = 2 ,
identifier = three ,
binExp = 4 + 5
2024-09-07 12:51:35 -04:00
}
2024-10-02 14:19:40 -05:00
yo = [
2024-09-07 12:51:35 -04:00
1 ,
" 2, " ,
" three " ,
4 + 5 ,
" hey oooooo really long long long "
]
" #;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! ( recasted , some_program_string ) ;
}
#[ test ]
fn test_recast_new_line_before_comment ( ) {
let some_program_string = r #"
// this is a comment
2024-11-25 09:21:55 +13:00
yo = { a = { b = { c = ' 123 ' } } }
2024-09-07 12:51:35 -04:00
2024-10-02 14:19:40 -05:00
key = 'c'
things = " things "
2024-09-07 12:51:35 -04:00
// this is also a comment"#;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
let expected = some_program_string . trim ( ) ;
// Currently new parser removes an empty line
let actual = recasted . trim ( ) ;
assert_eq! ( actual , expected ) ;
}
#[ test ]
fn test_recast_comment_tokens_inside_strings ( ) {
2024-10-02 14:19:40 -05:00
let some_program_string = r #" b = {
2024-11-25 09:21:55 +13:00
end = 141 ,
start = 125 ,
type_ = " NonCodeNode " ,
value = "
2024-09-07 12:51:35 -04:00
// a comment
"
} " #;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! ( recasted . trim ( ) , some_program_string . trim ( ) ) ;
}
#[ test ]
fn test_recast_array_new_line_in_pipe ( ) {
2024-10-02 14:19:40 -05:00
let some_program_string = r #" myVar = 3
myVar2 = 5
myVar3 = 6
myAng = 40
myAng2 = 134
2025-03-21 22:39:12 +13:00
part001 = startSketchOn ( XY )
2024-09-07 12:51:35 -04:00
| > startProfileAt ( [ 0 , 0 ] , % )
| > line ( [ 1 , 3.82 ] , % , $seg01 ) // ln-should-get-tag
2025-04-24 10:33:27 -04:00
| > angledLine ( angle = - foo ( seg01 , myVar , % ) , length = myVar ) // ln-lineTo-xAbsolute should use angleToMatchLengthX helper
| > angledLine ( angle = - bar ( seg01 , myVar , % ) , length = myVar ) // ln-lineTo-yAbsolute should use angleToMatchLengthY helper"#;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! ( recasted . trim ( ) , some_program_string ) ;
}
#[ test ]
fn test_recast_array_new_line_in_pipe_custom ( ) {
2024-10-02 14:19:40 -05:00
let some_program_string = r #" myVar = 3
myVar2 = 5
myVar3 = 6
myAng = 40
myAng2 = 134
2025-03-21 22:39:12 +13:00
part001 = startSketchOn ( XY )
2024-09-07 12:51:35 -04:00
| > startProfileAt ( [ 0 , 0 ] , % )
| > line ( [ 1 , 3.82 ] , % , $seg01 ) // ln-should-get-tag
2025-04-24 10:33:27 -04:00
| > angledLine ( angle = - foo ( seg01 , myVar , % ) , length = myVar ) // ln-lineTo-xAbsolute should use angleToMatchLengthX helper
| > angledLine ( angle = - bar ( seg01 , myVar , % ) , length = myVar ) // ln-lineTo-yAbsolute should use angleToMatchLengthY helper
2024-09-07 12:51:35 -04:00
" #;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
let recasted = program . recast (
& FormatOptions {
tab_size : 3 ,
use_tabs : false ,
insert_final_newline : true ,
} ,
0 ,
) ;
assert_eq! ( recasted , some_program_string ) ;
}
#[ test ]
fn test_recast_after_rename_std ( ) {
2025-03-21 22:39:12 +13:00
let some_program_string = r #" part001 = startSketchOn(XY)
2024-09-07 12:51:35 -04:00
| > startProfileAt ( [ 0.0000000000 , 5.0000000000 ] , % )
| > line ( [ 0.4900857016 , - 0.0240763666 ] , % )
2024-10-02 14:19:40 -05:00
part002 = " part002 "
things = [ part001 , 0.0 ]
blah = 1
foo = false
baz = { a : 1 , part001 : " thing " }
2024-09-07 12:51:35 -04:00
fn ghi = ( part001 ) = > {
return part001
}
" #;
2024-12-05 17:56:49 +13:00
let mut program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
program . rename_symbol ( " mySuperCoolPart " , 6 ) ;
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! (
recasted ,
2025-03-21 22:39:12 +13:00
r #" mySuperCoolPart = startSketchOn(XY)
2024-09-07 12:51:35 -04:00
| > startProfileAt ( [ 0.0 , 5.0 ] , % )
| > line ( [ 0.4900857016 , - 0.0240763666 ] , % )
2024-10-02 14:19:40 -05:00
part002 = " part002 "
things = [ mySuperCoolPart , 0.0 ]
blah = 1
foo = false
2024-11-25 09:21:55 +13:00
baz = { a = 1 , part001 = " thing " }
2024-09-07 12:51:35 -04:00
2024-11-27 15:46:58 +13:00
fn ghi ( part001 ) {
2024-09-07 12:51:35 -04:00
return part001
}
" #
) ;
}
#[ test ]
fn test_recast_after_rename_fn_args ( ) {
let some_program_string = r #" fn ghi = (x, y, z) => {
return x
} " #;
2024-12-05 17:56:49 +13:00
let mut program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
program . rename_symbol ( " newName " , 10 ) ;
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! (
recasted ,
2024-11-27 15:46:58 +13:00
r #" fn ghi(newName, y, z) {
2024-09-07 12:51:35 -04:00
return newName
}
" #
) ;
}
#[ test ]
fn test_recast_trailing_comma ( ) {
2025-03-21 22:39:12 +13:00
let some_program_string = r #" startSketchOn(XY)
2024-09-07 12:51:35 -04:00
| > startProfileAt ( [ 0 , 0 ] , % )
| > arc ( {
2024-11-25 09:21:55 +13:00
radius = 1 ,
angle_start = 0 ,
angle_end = 180 ,
2024-09-07 12:51:35 -04:00
} , % ) " #;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! (
recasted ,
2025-03-21 22:39:12 +13:00
r #" startSketchOn(XY)
2024-09-07 12:51:35 -04:00
| > startProfileAt ( [ 0 , 0 ] , % )
| > arc ( {
2024-11-25 09:21:55 +13:00
radius = 1 ,
angle_start = 0 ,
angle_end = 180
2024-09-07 12:51:35 -04:00
} , % )
" #
) ;
}
#[ test ]
fn test_recast_negative_var ( ) {
2024-10-02 14:19:40 -05:00
let some_program_string = r #" w = 20
l = 8
h = 10
2024-09-07 12:51:35 -04:00
2025-03-21 22:39:12 +13:00
firstExtrude = startSketchOn ( XY )
2024-09-07 12:51:35 -04:00
| > startProfileAt ( [ 0 , 0 ] , % )
| > line ( [ 0 , l ] , % )
| > line ( [ w , 0 ] , % )
| > line ( [ 0 , - l ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > close ( )
2024-09-07 12:51:35 -04:00
| > extrude ( h , % )
" #;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! (
recasted ,
2024-10-02 14:19:40 -05:00
r #" w = 20
l = 8
h = 10
2024-09-07 12:51:35 -04:00
2025-03-21 22:39:12 +13:00
firstExtrude = startSketchOn ( XY )
2024-09-07 12:51:35 -04:00
| > startProfileAt ( [ 0 , 0 ] , % )
| > line ( [ 0 , l ] , % )
| > line ( [ w , 0 ] , % )
| > line ( [ 0 , - l ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > close ( )
2024-09-07 12:51:35 -04:00
| > extrude ( h , % )
" #
) ;
}
#[ test ]
fn test_recast_multiline_comment ( ) {
2024-10-02 14:19:40 -05:00
let some_program_string = r #" w = 20
l = 8
h = 10
2024-09-07 12:51:35 -04:00
// This is my comment
// It has multiple lines
// And it's really long
2025-03-21 22:39:12 +13:00
firstExtrude = startSketchOn ( XY )
2024-09-07 12:51:35 -04:00
| > startProfileAt ( [ 0 , 0 ] , % )
| > line ( [ 0 , l ] , % )
| > line ( [ w , 0 ] , % )
| > line ( [ 0 , - l ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > close ( )
2024-09-07 12:51:35 -04:00
| > extrude ( h , % )
" #;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! (
recasted ,
2024-10-02 14:19:40 -05:00
r #" w = 20
l = 8
h = 10
2024-09-07 12:51:35 -04:00
// This is my comment
// It has multiple lines
// And it's really long
2025-03-21 22:39:12 +13:00
firstExtrude = startSketchOn ( XY )
2024-09-07 12:51:35 -04:00
| > startProfileAt ( [ 0 , 0 ] , % )
| > line ( [ 0 , l ] , % )
| > line ( [ w , 0 ] , % )
| > line ( [ 0 , - l ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > close ( )
2024-09-07 12:51:35 -04:00
| > extrude ( h , % )
" #
) ;
}
2025-02-27 15:58:58 +13:00
#[ test ]
fn test_recast_math_start_negative ( ) {
2024-10-02 14:19:40 -05:00
let some_program_string = r # "myVar = -5 + 6"# ;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! ( recasted . trim ( ) , some_program_string ) ;
}
2025-02-27 15:58:58 +13:00
#[ test ]
fn test_recast_math_negate_parens ( ) {
2024-10-02 14:19:40 -05:00
let some_program_string = r #" wallMountL = 3.82
thickness = 0.5
2024-09-07 12:51:35 -04:00
2025-03-21 22:39:12 +13:00
startSketchOn ( XY )
2024-09-07 12:51:35 -04:00
| > startProfileAt ( [ 0 , 0 ] , % )
| > line ( [ 0 , - ( wallMountL - thickness ) ] , % )
| > line ( [ 0 , - ( 5 - thickness ) ] , % )
| > line ( [ 0 , - ( 5 - 1 ) ] , % )
| > line ( [ 0 , - ( - 5 - 1 ) ] , % ) " #;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! ( recasted . trim ( ) , some_program_string ) ;
}
2025-02-27 15:58:58 +13:00
#[ test ]
fn test_recast_math_nested_parens ( ) {
2024-10-02 14:19:40 -05:00
let some_program_string = r #" distance = 5
2025-02-27 15:58:58 +13:00
p = 3 : Plane
FOS = { a = 3 , b = 42 } : Sketch
sigmaAllow = 8 : number ( mm )
2024-10-02 14:19:40 -05:00
width = 20
thickness = sqrt ( distance * p * FOS * 6 / ( sigmaAllow * width ) ) " #;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-10-02 14:19:40 -05:00
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! ( recasted . trim ( ) , some_program_string ) ;
}
2025-02-27 15:58:58 +13:00
#[ test ]
fn no_vardec_keyword ( ) {
2024-10-02 14:19:40 -05:00
let some_program_string = r # "distance = 5"# ;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! ( recasted . trim ( ) , some_program_string ) ;
}
2025-03-08 03:53:34 +13:00
#[ test ]
fn recast_types ( ) {
let some_program_string = r #" type foo
// A comment
@ ( impl = primitive )
export type bar ( unit , baz )
2025-03-21 10:56:55 +13:00
type baz = Foo | Bar
2025-03-08 03:53:34 +13:00
" #;
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
assert_eq! ( recasted , some_program_string ) ;
}
2024-11-20 08:23:30 -06:00
#[ test ]
fn recast_nested_fn ( ) {
let some_program_string = r #" fn f = () => {
2024-11-27 15:46:58 +13:00
return fn ( ) = > {
2024-11-20 08:23:30 -06:00
return 1
}
} " #;
2024-12-05 17:56:49 +13:00
let program = crate ::parsing ::top_level_parse ( some_program_string ) . unwrap ( ) ;
2024-11-20 08:23:30 -06:00
let recasted = program . recast ( & Default ::default ( ) , 0 ) ;
let expected = " \
2024-11-27 15:46:58 +13:00
fn f ( ) {
return fn ( ) {
2024-11-20 08:23:30 -06:00
return 1
}
} " ;
assert_eq! ( recasted . trim ( ) , expected ) ;
}
2024-09-07 12:51:35 -04:00
#[ test ]
fn recast_literal ( ) {
use winnow ::Parser ;
for ( i , ( raw , expected , reason ) ) in [
(
" 5.0 " ,
" 5.0 " ,
" fractional numbers should stay fractional, i.e. don't reformat this to '5' " ,
) ,
(
" 5 " ,
" 5 " ,
" integers should stay integral, i.e. don't reformat this to '5.0' " ,
) ,
(
" 5.0000000 " ,
" 5.0 " ,
" if the number is f64 but not fractional, use its canonical format " ,
) ,
( " 5.1 " , " 5.1 " , " straightforward case works " ) ,
]
. into_iter ( )
. enumerate ( )
{
2024-12-10 14:26:53 +13:00
let tokens = crate ::parsing ::token ::lex ( raw , ModuleId ::default ( ) ) . unwrap ( ) ;
let literal = crate ::parsing ::parser ::unsigned_number_literal
. parse ( tokens . as_slice ( ) )
. unwrap ( ) ;
2024-09-07 12:51:35 -04:00
assert_eq! (
literal . recast ( ) ,
expected ,
" failed test {i}, which is testing that {reason} "
) ;
}
}
#[ test ]
fn recast_objects_no_comments ( ) {
let input = r #"
2024-10-02 14:19:40 -05:00
sketch002 = startSketchOn ( {
2024-09-07 12:51:35 -04:00
plane : {
2024-11-25 09:21:55 +13:00
origin : { x = 1 , y = 2 , z = 3 } ,
2025-04-03 22:44:52 +13:00
x_axis = { x = 4 , y = 5 , z = 6 } ,
y_axis = { x = 7 , y = 8 , z = 9 } ,
z_axis = { x = 10 , y = 11 , z = 12 }
2024-09-07 12:51:35 -04:00
}
} )
" #;
2024-10-02 14:19:40 -05:00
let expected = r #" sketch002 = startSketchOn({
2024-11-25 09:21:55 +13:00
plane = {
origin = { x = 1 , y = 2 , z = 3 } ,
x_axis = { x = 4 , y = 5 , z = 6 } ,
y_axis = { x = 7 , y = 8 , z = 9 } ,
z_axis = { x = 10 , y = 11 , z = 12 }
2024-09-07 12:51:35 -04:00
}
} )
" #;
2024-12-05 17:56:49 +13:00
let ast = crate ::parsing ::top_level_parse ( input ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
let actual = ast . recast ( & FormatOptions ::new ( ) , 0 ) ;
assert_eq! ( actual , expected ) ;
}
Bug: KCL formatter removes 'fn' from closures: (#4718)
# Problem
Before this PR, our formatter reformats
```
squares_out = reduce(arr, 0, fn (i, squares) {
return 1
})
```
to
```
squares_out = reduce(arr, 0, (i, squares) {
return 1
})
```
i.e. it removes the `fn` keyword from the closure. This keyword is required, so, our formatter turned working code into invalid code.
# Cause
When this closure parameter is formatted, the ExprContext is ::Decl, so `Expr::recast` skips adding the `fn` keyword. The reason it's ::Decl is because the `squares_out = ` declaration sets it, and no subsequent call sets the context to something else.
# Solution
When recasting a call expression, set the context for every argument to `ExprContext::Other`.
2024-12-09 19:13:49 -06:00
#[ test ]
fn unparse_fn_unnamed ( ) {
2025-02-27 15:58:58 +13:00
let input = r #" squares_out = reduce(arr, 0: number, fn(i, squares) {
Bug: KCL formatter removes 'fn' from closures: (#4718)
# Problem
Before this PR, our formatter reformats
```
squares_out = reduce(arr, 0, fn (i, squares) {
return 1
})
```
to
```
squares_out = reduce(arr, 0, (i, squares) {
return 1
})
```
i.e. it removes the `fn` keyword from the closure. This keyword is required, so, our formatter turned working code into invalid code.
# Cause
When this closure parameter is formatted, the ExprContext is ::Decl, so `Expr::recast` skips adding the `fn` keyword. The reason it's ::Decl is because the `squares_out = ` declaration sets it, and no subsequent call sets the context to something else.
# Solution
When recasting a call expression, set the context for every argument to `ExprContext::Other`.
2024-12-09 19:13:49 -06:00
return 1
} )
" #;
let ast = crate ::parsing ::top_level_parse ( input ) . unwrap ( ) ;
let actual = ast . recast ( & FormatOptions ::new ( ) , 0 ) ;
assert_eq! ( actual , input ) ;
}
#[ test ]
fn unparse_fn_named ( ) {
let input = r #" fn f(x) {
return 1
}
" #;
let ast = crate ::parsing ::top_level_parse ( input ) . unwrap ( ) ;
let actual = ast . recast ( & FormatOptions ::new ( ) , 0 ) ;
assert_eq! ( actual , input ) ;
}
2024-09-07 12:51:35 -04:00
#[ test ]
fn recast_objects_with_comments ( ) {
use winnow ::Parser ;
for ( i , ( input , expected , reason ) ) in [ (
" \
{
2024-11-25 09:21:55 +13:00
a = 1 ,
// b = 2,
c = 3
2024-09-07 12:51:35 -04:00
} " ,
" \
{
2024-11-25 09:21:55 +13:00
a = 1 ,
// b = 2,
c = 3
2024-09-07 12:51:35 -04:00
} " ,
" preserves comments " ,
) ]
. into_iter ( )
. enumerate ( )
{
2024-12-10 14:26:53 +13:00
let tokens = crate ::parsing ::token ::lex ( input , ModuleId ::default ( ) ) . unwrap ( ) ;
crate ::parsing ::parser ::print_tokens ( tokens . as_slice ( ) ) ;
let expr = crate ::parsing ::parser ::object . parse ( tokens . as_slice ( ) ) . unwrap ( ) ;
2024-09-07 12:51:35 -04:00
assert_eq! (
2024-11-27 15:46:58 +13:00
expr . recast ( & FormatOptions ::new ( ) , 0 , ExprContext ::Other ) ,
2024-09-07 12:51:35 -04:00
expected ,
" failed test {i}, which is testing that recasting {reason} "
) ;
}
}
#[ test ]
fn recast_array_with_comments ( ) {
use winnow ::Parser ;
for ( i , ( input , expected , reason ) ) in [
(
" \
[
1 ,
2 ,
3 ,
4 ,
5 ,
6 ,
7 ,
8 ,
9 ,
10 ,
11 ,
12 ,
13 ,
14 ,
15 ,
16 ,
17 ,
18 ,
19 ,
20 ,
] " ,
" \
[
1 ,
2 ,
3 ,
4 ,
5 ,
6 ,
7 ,
8 ,
9 ,
10 ,
11 ,
12 ,
13 ,
14 ,
15 ,
16 ,
17 ,
18 ,
19 ,
20
] " ,
" preserves multi-line arrays " ,
) ,
(
" \
[
1 ,
// 2,
3
] " ,
" \
[
1 ,
// 2,
3
] " ,
" preserves comments " ,
) ,
(
" \
[
1 ,
2 ,
// 3
] " ,
" \
[
1 ,
2 ,
// 3
] " ,
" preserves comments at the end of the array " ,
) ,
]
. into_iter ( )
. enumerate ( )
{
2024-12-10 14:26:53 +13:00
let tokens = crate ::parsing ::token ::lex ( input , ModuleId ::default ( ) ) . unwrap ( ) ;
let expr = crate ::parsing ::parser ::array_elem_by_elem
. parse ( tokens . as_slice ( ) )
. unwrap ( ) ;
2024-09-07 12:51:35 -04:00
assert_eq! (
2024-11-27 15:46:58 +13:00
expr . recast ( & FormatOptions ::new ( ) , 0 , ExprContext ::Other ) ,
2024-09-07 12:51:35 -04:00
expected ,
" failed test {i}, which is testing that recasting {reason} "
) ;
}
}
2025-03-20 16:23:20 +13:00
#[ test ]
fn code_with_comment_and_extra_lines ( ) {
let code = r #" yo = 'c'
/* this is
a
comment * /
yo = ' bing '
" #;
let ast = crate ::parsing ::top_level_parse ( code ) . unwrap ( ) ;
let recasted = ast . recast ( & FormatOptions ::new ( ) , 0 ) ;
assert_eq! ( recasted , code ) ;
}
#[ test ]
fn comments_in_a_fn_block ( ) {
let code = r #" fn myFn() {
// this is a comment
yo = { a = { b = { c = ' 123 ' } } }
/* block
comment * /
key = 'c'
// this is also a comment
}
" #;
let ast = crate ::parsing ::top_level_parse ( code ) . unwrap ( ) ;
let recasted = ast . recast ( & FormatOptions ::new ( ) , 0 ) ;
assert_eq! ( recasted , code ) ;
}
2024-09-07 12:51:35 -04:00
}