Skip to content

Commit 503cdef

Browse files
committed
reuse lua parser
1 parent 2cec8d3 commit 503cdef

File tree

4 files changed

+142
-228
lines changed

4 files changed

+142
-228
lines changed

nattlua/definitions/lua/ffi/parser.lua

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
11
local META = require("nattlua.parser.base")()
22
require("nattlua.parser.expressions")(META)
33

4-
function META:ParseValueExpressionToken(expect_value--[[#: nil | string]])
5-
local node = self:StartNode("expression_value")
6-
node.value = expect_value and self:ExpectTokenValue(expect_value) or self:ParseToken()
7-
node = self:EndNode(node)
8-
return node
9-
end
10-
114
function META:ParseLSXExpression()
125
return nil
136
end

nattlua/definitions/lua/ffi/preprocessor/parser.lua

Lines changed: 121 additions & 210 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)