@@ -356,245 +356,156 @@ do -- conditional compilation (#if, #ifdef, #ifndef, #else, #elif, #endif)
356356 -- - #ifdef, #ifndef, #if, #elif, #else, #endif
357357 -- - Nested conditionals with proper depth tracking
358358 -- - Token removal for false branches
359- --
360- -- Known limitations:
361- -- - Expression evaluation with macro expansion and > operator has edge cases
362- -- - Multi-character operators (>=, ==, etc.) with complex expressions may fail
363- -- - See TODO comments in tests for specific failing cases
364- -- Helper to evaluate a simple expression (for #if and #elif)
365- -- This is a simplified evaluator that handles:
366- -- - defined(MACRO) and defined MACRO operators
367- -- - Integer literals
368- -- - Basic arithmetic and comparison operators
369- -- - Logical operators (&&, ||, !)
370- local function evaluate_condition (self , tokens )
371- -- Simple recursive descent parser for constant expressions
372- local pos = 1
373-
374- local function peek ()
375- return tokens [pos ]
376- end
377-
378- local function advance ()
379- pos = pos + 1
380- return tokens [pos - 1 ]
381- end
382-
383- local function parse_primary ()
384- local tk = peek ()
385-
386- if not tk then return 0 end
387-
388- -- Handle defined(X) or defined X
389- if tk :ValueEquals (" defined" ) then
390- advance () -- consume 'defined'
391- local has_paren = peek () and peek ():ValueEquals (" (" )
392-
393- if has_paren then advance () end -- consume '('
394- local name_tk = advance ()
395-
396- if not name_tk then return 0 end
397-
398- local is_defined = self :GetDefinition (name_tk :GetValueString ()) ~= nil
399-
400- if has_paren then
401- if peek () and peek ():ValueEquals (" )" ) then
402- advance () -- consume ')'
403- end
359+ -- - Uses the project's existing Lua parser for expression evaluation
360+ -- Inline expression parser using the project's parser infrastructure
361+ do
362+ -- Create parser meta once and reuse it
363+ local parser_meta = require (" nattlua.parser.base" )()
364+ require (" nattlua.parser.expressions" )(parser_meta )
365+
366+ -- Evaluator for the AST
367+ local function evaluate_ast (self , node )
368+ if not node then return 0 end
369+
370+ if node .Type == " expression_value" then
371+ local tk = node .value
372+
373+ -- Handle numbers
374+ if tk .type == " number" then
375+ return tonumber (tk :GetValueString ()) or 0
404376 end
405377
406- return is_defined and 1 or 0
407- end
408-
409- -- Handle numbers
410- if tk .type == " number" then
411- advance ()
412- return tonumber (tk :GetValueString ()) or 0
413- end
414-
415- -- Handle parentheses
416- if tk :ValueEquals (" (" ) then
417- advance () -- consume '('
418- local val = parse_logical_or ()
419-
420- if peek () and peek ():ValueEquals (" )" ) then advance () -- consume ')'
378+ -- Handle defined operator
379+ if tk :ValueEquals (" defined" ) then
380+ return 0 -- This should be handled by prefix operator
421381 end
422382
423- return val
424- end
425-
426- -- Handle unary operators
427- if tk :ValueEquals (" !" ) then
428- advance ()
429- local val = parse_primary ()
430- return val == 0 and 1 or 0
431- end
432-
433- if tk :ValueEquals (" -" ) then
434- advance ()
435- return - parse_primary ()
436- end
437-
438- if tk :ValueEquals (" +" ) then
439- advance ()
440- return parse_primary ()
441- end
383+ -- Handle identifiers (check if it's a macro)
384+ if tk .type == " letter" then
385+ local def = self :GetDefinition (tk :GetValueString ())
442386
443- -- Undefined identifiers evaluate to 0
444- if tk .type == " letter" then
445- advance ()
446- local def = self :GetDefinition (tk :GetValueString ())
387+ if def and def .tokens [1 ] and def .tokens [1 ].type == " number" then
388+ return tonumber (def .tokens [1 ]:GetValueString ()) or 0
389+ end
447390
448- if def and def .tokens [1 ] and def .tokens [1 ].type == " number" then
449- return tonumber (def .tokens [1 ]:GetValueString ()) or 0
391+ return 0 -- Undefined identifiers evaluate to 0
450392 end
451393
452394 return 0
453- end
395+ elseif node .Type == " expression_prefix_operator" then
396+ local op = node .value :GetValueString ()
397+ local right = evaluate_ast (self , node .right )
398+
399+ if op == " !" or op == " not" then
400+ return right == 0 and 1 or 0
401+ elseif op == " -" then
402+ return - right
403+ elseif op == " +" then
404+ return right
405+ end
454406
455- advance () -- skip unknown token
456- return 0
457- end
407+ return 0
408+ elseif node . Type == " expression_binary_operator " then
409+ local op = node . value : GetValueString ()
458410
459- local function parse_multiplicative ()
460- local left = parse_primary ()
461-
462- while peek () do
463- local op = peek ()
464-
465- if op :ValueEquals (" *" ) then
466- advance ()
467- left = left * parse_primary ()
468- elseif op :ValueEquals (" /" ) then
469- advance ()
470- local right = parse_primary ()
471- left = right ~= 0 and (left / right ) or 0
472- elseif op :ValueEquals (" %" ) then
473- advance ()
474- local right = parse_primary ()
475- left = right ~= 0 and (left % right ) or 0
476- else
477- break
411+ -- Handle 'defined' operator specially
412+ if op == " defined" then
413+ -- This shouldn't happen in well-formed code
414+ return 0
478415 end
479- end
480416
481- return left
482- end
417+ local left = evaluate_ast (self , node .left )
483418
484- local function parse_additive ()
485- local left = parse_multiplicative ()
419+ -- Short-circuit evaluation for logical operators
420+ if op == " &&" or op == " and" then
421+ if left == 0 then return 0 end
486422
487- while peek () do
488- local op = peek ()
423+ local right = evaluate_ast (self , node .right )
424+ return (left ~= 0 and right ~= 0 ) and 1 or 0
425+ elseif op == " ||" or op == " or" then
426+ if left ~= 0 then return 1 end
489427
490- if op :ValueEquals (" +" ) then
491- advance ()
492- left = left + parse_multiplicative ()
493- elseif op :ValueEquals (" -" ) then
494- advance ()
495- left = left - parse_multiplicative ()
496- else
497- break
428+ local right = evaluate_ast (self , node .right )
429+ return (left ~= 0 or right ~= 0 ) and 1 or 0
498430 end
499- end
500431
501- return left
502- end
503-
504- local function parse_relational ()
505- local left = parse_additive ()
506-
507- while peek () do
508- local op = peek ()
509- local next_op = tokens [pos + 1 ]
510-
511- if op :ValueEquals (" <" ) and next_op and next_op :ValueEquals (" =" ) then
512- advance () -- consume <
513- advance () -- consume =
514- left = left <= parse_additive () and 1 or 0
515- elseif op :ValueEquals (" >" ) and next_op and next_op :ValueEquals (" =" ) then
516- advance () -- consume >
517- advance () -- consume =
518- left = left >= parse_additive () and 1 or 0
519- elseif op :ValueEquals (" <" ) then
520- advance ()
521- left = left < parse_additive () and 1 or 0
522- elseif op :ValueEquals (" >" ) then
523- advance ()
524- left = left > parse_additive () and 1 or 0
525- else
526- break
432+ local right = evaluate_ast (self , node .right )
433+
434+ if op == " +" then
435+ return left + right
436+ elseif op == " -" then
437+ return left - right
438+ elseif op == " *" then
439+ return left * right
440+ elseif op == " /" then
441+ return right ~= 0 and (left / right ) or 0
442+ elseif op == " %" then
443+ return right ~= 0 and (left % right ) or 0
444+ elseif op == " ==" then
445+ return left == right and 1 or 0
446+ elseif op == " ~=" or op == " !=" then
447+ return left ~= right and 1 or 0
448+ elseif op == " <" then
449+ return left < right and 1 or 0
450+ elseif op == " >" then
451+ return left > right and 1 or 0
452+ elseif op == " <=" then
453+ return left <= right and 1 or 0
454+ elseif op == " >=" then
455+ return left >= right and 1 or 0
527456 end
528- end
529457
530- return left
531- end
532-
533- local function parse_equality ()
534- local left = parse_relational ()
535-
536- while peek () do
537- local op = peek ()
538- local next_op = tokens [pos + 1 ]
539-
540- if op :ValueEquals (" =" ) and next_op and next_op :ValueEquals (" =" ) then
541- advance () -- consume =
542- advance () -- consume =
543- left = left == parse_relational () and 1 or 0
544- elseif op :ValueEquals (" !" ) and next_op and next_op :ValueEquals (" =" ) then
545- advance () -- consume !
546- advance () -- consume =
547- left = left ~= parse_relational () and 1 or 0
548- else
549- break
550- end
458+ return 0
551459 end
552460
553- return left
461+ return 0
554462 end
555463
556- local function parse_logical_and ()
557- local left = parse_equality ()
558-
559- while peek () do
560- local op = peek ()
561- local next_op = tokens [pos + 1 ]
562-
563- if op :ValueEquals (" &" ) and next_op and next_op :ValueEquals (" &" ) then
564- advance () -- consume &
565- advance () -- consume &
566- local right = parse_equality ()
567- left = (left ~= 0 and right ~= 0 ) and 1 or 0
464+ -- Main evaluation function
465+ function META :EvaluateCondition (tokens )
466+ -- Handle 'defined' operator preprocessing
467+ -- Convert "defined(X)" or "defined X" into a numeric value
468+ local processed_tokens = {}
469+ local i = 1
470+
471+ while i <= # tokens do
472+ local tk = tokens [i ]
473+
474+ if tk :ValueEquals (" defined" ) then
475+ -- Check if next token is '('
476+ local has_paren = tokens [i + 1 ] and tokens [i + 1 ]:ValueEquals (" (" )
477+ local name_idx = has_paren and i + 2 or i + 1
478+ local name_tk = tokens [name_idx ]
479+
480+ if name_tk then
481+ local is_defined = self :GetDefinition (name_tk :GetValueString ()) ~= nil
482+ local value_token = self :NewToken (" number" , is_defined and " 1" or " 0" )
483+ table.insert (processed_tokens , value_token )
484+ -- Skip past the defined operator and its argument
485+ i = name_idx + 1
486+
487+ if has_paren and tokens [i ] and tokens [i ]:ValueEquals (" )" ) then
488+ i = i + 1
489+ end
490+ else
491+ i = i + 1
492+ end
568493 else
569- break
494+ table.insert (processed_tokens , tk )
495+ i = i + 1
570496 end
571497 end
572498
573- return left
574- end
575-
576- function parse_logical_or ()
577- local left = parse_logical_and ()
499+ -- Parse the expression
500+ local parser = parser_meta .New (processed_tokens , self .Code )
501+ local ast = parser :ParseRuntimeExpression (0 )
578502
579- while peek () do
580- local op = peek ()
581- local next_op = tokens [pos + 1 ]
503+ if not ast then return false end
582504
583- if op :ValueEquals (" |" ) and next_op and next_op :ValueEquals (" |" ) then
584- advance () -- consume |
585- advance () -- consume |
586- local right = parse_logical_and ()
587- left = (left ~= 0 or right ~= 0 ) and 1 or 0
588- else
589- break
590- end
591- end
592-
593- return left
505+ -- Evaluate the AST
506+ local result = evaluate_ast (self , ast )
507+ return result ~= 0
594508 end
595-
596- local result = parse_logical_or ()
597- return result ~= 0
598509 end
599510
600511 -- Helper to skip tokens until we find a matching directive
@@ -735,7 +646,7 @@ do -- conditional compilation (#if, #ifdef, #ifndef, #else, #elif, #endif)
735646 self :ExpectToken (" #" )
736647 self :ExpectToken (" if" )
737648 local tokens = self :CaptureTokens ()
738- local condition = evaluate_condition ( self , tokens )
649+ local condition = self : EvaluateCondition ( tokens )
739650 table.insert (self .conditional_stack , {active = condition , had_true = condition })
740651 self :RemoveDirectiveTokens ()
741652
@@ -765,7 +676,7 @@ do -- conditional compilation (#if, #ifdef, #ifndef, #else, #elif, #endif)
765676 skip_until_directive (self , {" else" , " elif" , " endif" })
766677 else
767678 -- Evaluate the elif condition
768- local condition = evaluate_condition ( self , tokens )
679+ local condition = self : EvaluateCondition ( tokens )
769680 state .active = condition
770681 state .had_true = condition
771682
0 commit comments