mirror of
https://github.com/Alvin-Zilverstand/femcode.git
synced 2026-03-06 13:23:39 +01:00
feat: Implement logical operators (AND, OR, NOT)
This commit is contained in:
20
examples/logical_ops.fem
Normal file
20
examples/logical_ops.fem
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
my_true is Kawaii
|
||||||
|
my_false is Cringe
|
||||||
|
|
||||||
|
Femboy Feminine my_true and my_false Femboycore
|
||||||
|
UwU Boy "This won't print."
|
||||||
|
Periodt
|
||||||
|
Androgyny Femboycore
|
||||||
|
UwU Boy "True AND False is False."
|
||||||
|
Periodt
|
||||||
|
|
||||||
|
Femboy Feminine my_true or my_false Femboycore
|
||||||
|
UwU Boy "True OR False is True."
|
||||||
|
Periodt
|
||||||
|
|
||||||
|
Femboy Feminine not my_true Femboycore
|
||||||
|
UwU Boy "This won't print."
|
||||||
|
Periodt
|
||||||
|
Androgyny Femboycore
|
||||||
|
UwU Boy "NOT True is False."
|
||||||
|
Periodt
|
||||||
@@ -29,6 +29,16 @@ class Interpreter:
|
|||||||
def visit_Boolean(self, node):
|
def visit_Boolean(self, node):
|
||||||
return node.value
|
return node.value
|
||||||
|
|
||||||
|
def visit_LogicalOp(self, node):
|
||||||
|
if node.op.type == 'AND':
|
||||||
|
return self.visit(node.left) and self.visit(node.right)
|
||||||
|
elif node.op.type == 'OR':
|
||||||
|
return self.visit(node.left) or self.visit(node.right)
|
||||||
|
|
||||||
|
def visit_UnaryOp(self, node):
|
||||||
|
if node.op.type == 'NOT':
|
||||||
|
return not self.visit(node.right)
|
||||||
|
|
||||||
def visit_BinOp(self, node):
|
def visit_BinOp(self, node):
|
||||||
left_val = self.visit(node.left)
|
left_val = self.visit(node.left)
|
||||||
right_val = self.visit(node.right)
|
right_val = self.visit(node.right)
|
||||||
|
|||||||
@@ -131,6 +131,15 @@ class Lexer:
|
|||||||
if re.match(r'\bCringe\b', self.text[self.pos:]):
|
if re.match(r'\bCringe\b', self.text[self.pos:]):
|
||||||
self.pos += len('Cringe')
|
self.pos += len('Cringe')
|
||||||
return Token('CRINGE', False)
|
return Token('CRINGE', False)
|
||||||
|
if re.match(r'\band\b', self.text[self.pos:]):
|
||||||
|
self.pos += len('and')
|
||||||
|
return Token('AND', 'and')
|
||||||
|
if re.match(r'\bor\b', self.text[self.pos:]):
|
||||||
|
self.pos += len('or')
|
||||||
|
return Token('OR', 'or')
|
||||||
|
if re.match(r'\bnot\b', self.text[self.pos:]):
|
||||||
|
self.pos += len('not')
|
||||||
|
return Token('NOT', 'not')
|
||||||
|
|
||||||
# Match identifiers
|
# Match identifiers
|
||||||
match = re.match(r'\b[a-zA-Z_][a-zA-Z0-9_]*\b', self.text[self.pos:])
|
match = re.match(r'\b[a-zA-Z_][a-zA-Z0-9_]*\b', self.text[self.pos:])
|
||||||
|
|||||||
@@ -16,6 +16,17 @@ class Boolean(AST):
|
|||||||
self.token = token
|
self.token = token
|
||||||
self.value = token.value
|
self.value = token.value
|
||||||
|
|
||||||
|
class LogicalOp(AST):
|
||||||
|
def __init__(self, left, op, right=None):
|
||||||
|
self.left = left
|
||||||
|
self.op = op
|
||||||
|
self.right = right
|
||||||
|
|
||||||
|
class UnaryOp(AST):
|
||||||
|
def __init__(self, op, right):
|
||||||
|
self.op = op
|
||||||
|
self.right = right
|
||||||
|
|
||||||
class BinOp(AST):
|
class BinOp(AST):
|
||||||
def __init__(self, left, op, right):
|
def __init__(self, left, op, right):
|
||||||
self.left = left
|
self.left = left
|
||||||
@@ -240,13 +251,20 @@ class Parser:
|
|||||||
return Number(token)
|
return Number(token)
|
||||||
elif token.type == 'STRING':
|
elif token.type == 'STRING':
|
||||||
return String(token) # Now returns a String AST node
|
return String(token) # Now returns a String AST node
|
||||||
|
elif token.type == 'KAWAII' or token.type == 'CRINGE':
|
||||||
|
return Boolean(token)
|
||||||
elif token.type == 'ID':
|
elif token.type == 'ID':
|
||||||
# Check for function call
|
# Check for function call
|
||||||
if self.peek_next_token().type == 'LPAREN': # Assuming '(' is the next token for a function call
|
if self.peek_next_token().type == 'LPAREN': # Assuming '(' is the next token for a function call
|
||||||
return self.parse_function_call(token)
|
return self.parse_function_call(token)
|
||||||
return Variable(token)
|
return Variable(token)
|
||||||
elif token.type == 'KAWAII' or token.type == 'CRINGE':
|
elif token.type == 'LPAREN': # Handle parenthesized expressions
|
||||||
return Boolean(token)
|
node = self.expression()
|
||||||
|
if self.get_next_token().type != 'RPAREN':
|
||||||
|
raise Exception("Expected ')'")
|
||||||
|
return node
|
||||||
|
elif token.type == 'NOT': # Handle NOT operator
|
||||||
|
return UnaryOp(token, self.factor()) # NOT applies to the next factor/expression
|
||||||
else:
|
else:
|
||||||
raise Exception(f"Expected integer, string, boolean or identifier, got {token.type}")
|
raise Exception(f"Expected integer, string, boolean or identifier, got {token.type}")
|
||||||
|
|
||||||
@@ -257,7 +275,7 @@ class Parser:
|
|||||||
node = BinOp(left=node, op=token, right=self.factor())
|
node = BinOp(left=node, op=token, right=self.factor())
|
||||||
return node
|
return node
|
||||||
|
|
||||||
def expression(self):
|
def comparison_expression(self):
|
||||||
node = self.term()
|
node = self.term()
|
||||||
|
|
||||||
# Handle addition/subtraction
|
# Handle addition/subtraction
|
||||||
@@ -268,11 +286,19 @@ class Parser:
|
|||||||
# Handle comparisons
|
# Handle comparisons
|
||||||
if self.peek_next_token().type in ('EQ', 'NEQ', 'GT', 'GTE', 'LT', 'LTE'):
|
if self.peek_next_token().type in ('EQ', 'NEQ', 'GT', 'GTE', 'LT', 'LTE'):
|
||||||
op_token = self.get_next_token()
|
op_token = self.get_next_token()
|
||||||
right_node = self.expression() # Recursively parse right side of comparison
|
right_node = self.comparison_expression() # Recursively parse right side of comparison
|
||||||
node = Comparison(left=node, op=op_token, right=right_node)
|
node = Comparison(left=node, op=op_token, right=right_node)
|
||||||
|
|
||||||
return node
|
return node
|
||||||
|
|
||||||
|
def expression(self):
|
||||||
|
node = self.comparison_expression()
|
||||||
|
while self.peek_next_token().type in ('AND', 'OR'):
|
||||||
|
op_token = self.get_next_token()
|
||||||
|
right_node = self.comparison_expression()
|
||||||
|
node = LogicalOp(left=node, op=op_token, right=right_node)
|
||||||
|
return node
|
||||||
|
|
||||||
def parse_function_call(self, name_token):
|
def parse_function_call(self, name_token):
|
||||||
self.get_next_token() # Consume '('
|
self.get_next_token() # Consume '('
|
||||||
arguments = []
|
arguments = []
|
||||||
@@ -284,4 +310,4 @@ class Parser:
|
|||||||
else:
|
else:
|
||||||
break # Exit loop if not COMMA (implies RPAREN or EOF)
|
break # Exit loop if not COMMA (implies RPAREN or EOF)
|
||||||
self.get_next_token() # Consume ')'
|
self.get_next_token() # Consume ')'
|
||||||
return FunctionCall(name_token.value, arguments)
|
return FunctionCall(name_token.value, arguments)
|
||||||
Reference in New Issue
Block a user