@@ -410,14 +410,13 @@ namespace Ark::internal
410410 else if (name == " $len" )
411411 {
412412 checkMacroArgCountEq (node, 1 , " $len" , true );
413+ Node& lst = node.list ()[1 ];
414+ checkMacroTypeError (" $len" , " node" , NodeType::List, lst);
413415
414- if (Node& lst = node.list ()[1 ]; lst.nodeType () == NodeType::List) // only apply len at compile time if we can
415- {
416- if (!lst.list ().empty () && lst.list ()[0 ] == getListNode ())
417- node.updateValueAndType (Node (static_cast <long >(lst.list ().size ()) - 1 ));
418- else
419- node.updateValueAndType (Node (static_cast <long >(lst.list ().size ())));
420- }
416+ if (!lst.list ().empty () && lst.list ()[0 ] == getListNode ())
417+ node.updateValueAndType (Node (static_cast <long >(lst.list ().size ()) - 1 ));
418+ else
419+ node.updateValueAndType (Node (static_cast <long >(lst.list ().size ())));
421420 }
422421 else if (name == " $empty?" )
423422 {
@@ -441,79 +440,64 @@ namespace Ark::internal
441440 Node sublist = evaluate (node.list ()[1 ], depth + 1 , is_not_body);
442441 const Node idx = evaluate (node.list ()[2 ], depth + 1 , is_not_body);
443442
444- if (sublist.nodeType () == NodeType::List && idx.nodeType () == NodeType::Number)
445- {
446- const std::size_t size = sublist.list ().size ();
447- std::size_t real_size = size;
448- long num_idx = static_cast <long >(idx.number ());
449-
450- // if the first node is the function call to "list", don't count it
451- if (size > 0 && sublist.list ()[0 ] == getListNode ())
452- {
453- real_size--;
454- if (num_idx >= 0 )
455- ++num_idx;
456- }
443+ checkMacroTypeError (" $at" , " list" , NodeType::List, sublist);
444+ checkMacroTypeError (" $at" , " index" , NodeType::Number, idx);
457445
458- Node output;
459- if (num_idx >= 0 && std::cmp_less (num_idx, size))
460- output = sublist.list ()[static_cast <std::size_t >(num_idx)];
461- else if (const auto c = static_cast <long >(size) + num_idx; num_idx < 0 && std::cmp_less (c, size) && c >= 0 )
462- output = sublist.list ()[static_cast <std::size_t >(c)];
463- else
464- throwMacroProcessingError (fmt::format (" Index ({}) out of range (list size: {})" , num_idx, real_size), node);
446+ const std::size_t size = sublist.list ().size ();
447+ std::size_t real_size = size;
448+ long num_idx = static_cast <long >(idx.number ());
465449
466- output.setPositionFrom (node);
467- return output;
450+ // if the first node is the function call to "list", don't count it
451+ if (size > 0 && sublist.list ()[0 ] == getListNode ())
452+ {
453+ real_size--;
454+ if (num_idx >= 0 )
455+ ++num_idx;
468456 }
457+
458+ Node output;
459+ if (num_idx >= 0 && std::cmp_less (num_idx, size))
460+ output = sublist.list ()[static_cast <std::size_t >(num_idx)];
461+ else if (const auto c = static_cast <long >(size) + num_idx; num_idx < 0 && std::cmp_less (c, size) && c >= 0 )
462+ output = sublist.list ()[static_cast <std::size_t >(c)];
463+ else
464+ throwMacroProcessingError (fmt::format (" Index ({}) out of range (list size: {})" , num_idx, real_size), node);
465+
466+ output.setPositionFrom (node);
467+ return output;
469468 }
470469 else if (name == " $head" )
471470 {
472471 checkMacroArgCountEq (node, 1 , " $head" , true );
472+ Node sublist = node.list ()[1 ];
473+ checkMacroTypeError (" $head" , " node" , NodeType::List, sublist);
473474
474- if (node. list ()[ 1 ]. nodeType () == NodeType::List )
475+ if (!sublist. constList (). empty () && sublist. constList ()[ 0 ] == getListNode () )
475476 {
476- Node& sublist = node.list ()[1 ];
477- if (!sublist.constList ().empty () && sublist.constList ()[0 ] == getListNode ())
477+ if (sublist.constList ().size () > 1 )
478478 {
479- if (sublist.constList ().size () > 1 )
480- {
481- const Node sublistCopy = sublist.constList ()[1 ];
482- node.updateValueAndType (sublistCopy);
483- }
484- else
485- node.updateValueAndType (getNilNode ());
479+ const Node sublistCopy = sublist.constList ()[1 ];
480+ node.updateValueAndType (sublistCopy);
486481 }
487- else if (!sublist.list ().empty ())
488- node.updateValueAndType (sublist.constList ()[0 ]);
489482 else
490483 node.updateValueAndType (getNilNode ());
491484 }
485+ else if (!sublist.list ().empty ())
486+ node.updateValueAndType (sublist.constList ()[0 ]);
487+ else
488+ node.updateValueAndType (getNilNode ());
492489 }
493490 else if (name == " $tail" )
494491 {
495492 checkMacroArgCountEq (node, 1 , " $tail" , true );
493+ Node sublist = node.list ()[1 ];
494+ checkMacroTypeError (" $tail" , " node" , NodeType::List, sublist);
496495
497- if (node .list ()[ 1 ]. nodeType () == NodeType::List )
496+ if (!sublist .list (). empty () && sublist. list ()[ 0 ] == getListNode () )
498497 {
499- Node sublist = node.list ()[1 ];
500- if (!sublist.list ().empty () && sublist.list ()[0 ] == getListNode ())
501- {
502- if (sublist.list ().size () > 1 )
503- {
504- sublist.list ().erase (sublist.constList ().begin () + 1 );
505- node.updateValueAndType (sublist);
506- }
507- else
508- {
509- node.updateValueAndType (Node (NodeType::List));
510- node.push_back (getListNode ());
511- }
512- }
513- else if (!sublist.list ().empty ())
498+ if (sublist.list ().size () > 1 )
514499 {
515- sublist.list ().erase (sublist.constList ().begin ());
516- sublist.list ().insert (sublist.list ().begin (), getListNode ());
500+ sublist.list ().erase (sublist.constList ().begin () + 1 );
517501 node.updateValueAndType (sublist);
518502 }
519503 else
@@ -522,19 +506,23 @@ namespace Ark::internal
522506 node.push_back (getListNode ());
523507 }
524508 }
509+ else if (!sublist.list ().empty ())
510+ {
511+ sublist.list ().erase (sublist.constList ().begin ());
512+ sublist.list ().insert (sublist.list ().begin (), getListNode ());
513+ node.updateValueAndType (sublist);
514+ }
515+ else
516+ {
517+ node.updateValueAndType (Node (NodeType::List));
518+ node.push_back (getListNode ());
519+ }
525520 }
526521 else if (name == Language::Symcat)
527522 {
528523 if (node.list ().size () <= 2 )
529524 throwMacroProcessingError (fmt::format (" When expanding `{}', expected at least 2 arguments, got {} arguments" , Language::Symcat, argcount), node);
530- if (node.list ()[1 ].nodeType () != NodeType::Symbol)
531- throwMacroProcessingError (
532- fmt::format (
533- " When expanding `{}', expected the first argument to be a Symbol, got a {}: {}" ,
534- Language::Symcat,
535- typeToString (node.list ()[1 ]),
536- node.list ()[1 ].repr ()),
537- node.list ()[1 ]);
525+ checkMacroTypeError (Language::Symcat.data (), " symbol" , NodeType::Symbol, node.list ()[1 ]);
538526
539527 std::string sym = node.list ()[1 ].string ();
540528
@@ -735,4 +723,18 @@ namespace Ark::internal
735723
736724 throw CodeError (message, CodeErrorContext (node.filename (), node.position ()), maybe_context);
737725 }
726+
727+ void MacroProcessor::checkMacroTypeError (const std::string& macro, const std::string& arg, const NodeType expected, const Node& actual) const
728+ {
729+ if (actual.nodeType () != expected)
730+ throwMacroProcessingError (
731+ fmt::format (
732+ " When expanding `{}', expected '{}' to be a {}, got a {}: {}" ,
733+ macro,
734+ arg,
735+ std::string (nodeTypes[static_cast <std::size_t >(expected)]),
736+ typeToString (actual),
737+ actual.repr ()),
738+ actual);
739+ }
738740}
0 commit comments