mirror of
https://github.com/Alvin-Zilverstand/femcode.git
synced 2026-03-06 13:23:39 +01:00
feat: Implement dictionaries and property access
This commit is contained in:
3
examples/dictionaries.fem
Normal file
3
examples/dictionaries.fem
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
my_dict is {"name": "Femboy", "version": 1}
|
||||||
|
UwU Boy my_dict.name
|
||||||
|
UwU Boy my_dict.version
|
||||||
@@ -99,6 +99,34 @@ class Interpreter:
|
|||||||
while self.visit(node.condition):
|
while self.visit(node.condition):
|
||||||
self.visit(node.body)
|
self.visit(node.body)
|
||||||
|
|
||||||
|
def visit_List(self, node):
|
||||||
|
elements = [self.visit(element) for element in node.elements]
|
||||||
|
return elements
|
||||||
|
|
||||||
|
def visit_IndexAccess(self, node):
|
||||||
|
target = self.visit(node.target)
|
||||||
|
index = self.visit(node.index)
|
||||||
|
if isinstance(target, list):
|
||||||
|
return target[index]
|
||||||
|
else:
|
||||||
|
raise TypeError(f"Cannot index type {type(target).__name__}")
|
||||||
|
|
||||||
|
def visit_Dictionary(self, node):
|
||||||
|
dictionary = {}
|
||||||
|
for key_expr, value_expr in node.pairs:
|
||||||
|
key = self.visit(key_expr)
|
||||||
|
value = self.visit(value_expr)
|
||||||
|
dictionary[key] = value
|
||||||
|
return dictionary
|
||||||
|
|
||||||
|
def visit_PropertyAccess(self, node):
|
||||||
|
target = self.visit(node.target)
|
||||||
|
property_name = node.property_name
|
||||||
|
if isinstance(target, dict):
|
||||||
|
return target.get(property_name)
|
||||||
|
else:
|
||||||
|
raise TypeError(f"Cannot access property '{property_name}' on type {type(target).__name__}")
|
||||||
|
|
||||||
def visit_FunctionDefinition(self, node):
|
def visit_FunctionDefinition(self, node):
|
||||||
self.functions[node.name] = {
|
self.functions[node.name] = {
|
||||||
'parameters': node.parameters,
|
'parameters': node.parameters,
|
||||||
|
|||||||
22
src/lexer.py
22
src/lexer.py
@@ -13,8 +13,8 @@ class Lexer:
|
|||||||
self.text = text
|
self.text = text
|
||||||
self.pos = 0
|
self.pos = 0
|
||||||
|
|
||||||
def error(self):
|
def error(self, message="Invalid character"):
|
||||||
raise Exception('Invalid character')
|
raise Exception(f"{message} at position {self.pos}: '{self.text[self.pos]}'")
|
||||||
|
|
||||||
def get_next_token(self):
|
def get_next_token(self):
|
||||||
if self.pos > len(self.text) - 1:
|
if self.pos > len(self.text) - 1:
|
||||||
@@ -57,6 +57,24 @@ class Lexer:
|
|||||||
if current_char == ')':
|
if current_char == ')':
|
||||||
self.pos += 1
|
self.pos += 1
|
||||||
return Token('RPAREN', ')')
|
return Token('RPAREN', ')')
|
||||||
|
if current_char == '[':
|
||||||
|
self.pos += 1
|
||||||
|
return Token('LBRACKET', '[')
|
||||||
|
if current_char == ']':
|
||||||
|
self.pos += 1
|
||||||
|
return Token('RBRACKET', ']')
|
||||||
|
if current_char == '{':
|
||||||
|
self.pos += 1
|
||||||
|
return Token('LBRACE', '{')
|
||||||
|
if current_char == '}':
|
||||||
|
self.pos += 1
|
||||||
|
return Token('RBRACE', '}')
|
||||||
|
if current_char == ':':
|
||||||
|
self.pos += 1
|
||||||
|
return Token('COLON', ':')
|
||||||
|
if current_char == '.':
|
||||||
|
self.pos += 1
|
||||||
|
return Token('DOT', '.')
|
||||||
|
|
||||||
# Operators
|
# Operators
|
||||||
if current_char == '+':
|
if current_char == '+':
|
||||||
|
|||||||
262
src/parser.py
262
src/parser.py
@@ -84,31 +84,48 @@ class ReturnStatement(AST):
|
|||||||
def __init__(self, value):
|
def __init__(self, value):
|
||||||
self.value = value
|
self.value = value
|
||||||
|
|
||||||
|
class List(AST):
|
||||||
|
def __init__(self, elements):
|
||||||
|
self.elements = elements
|
||||||
|
|
||||||
|
class IndexAccess(AST):
|
||||||
|
def __init__(self, target, index):
|
||||||
|
self.target = target
|
||||||
|
self.index = index
|
||||||
|
|
||||||
|
class Dictionary(AST):
|
||||||
|
def __init__(self, pairs):
|
||||||
|
self.pairs = pairs # List of (key_expr, value_expr) tuples
|
||||||
|
|
||||||
|
class PropertyAccess(AST):
|
||||||
|
def __init__(self, target, property_name):
|
||||||
|
self.target = target
|
||||||
|
self.property_name = property_name
|
||||||
|
|
||||||
class Parser:
|
class Parser:
|
||||||
def __init__(self, tokens):
|
def __init__(self, tokens):
|
||||||
self.tokens = tokens
|
self.tokens = tokens
|
||||||
self.pos = 0
|
self.pos = 0
|
||||||
|
|
||||||
def get_next_token(self):
|
def get_current_token(self):
|
||||||
if self.pos < len(self.tokens):
|
|
||||||
token = self.tokens[self.pos]
|
|
||||||
self.pos += 1
|
|
||||||
return token
|
|
||||||
return Token('EOF', None)
|
|
||||||
|
|
||||||
def peek_next_token(self):
|
|
||||||
if self.pos < len(self.tokens):
|
if self.pos < len(self.tokens):
|
||||||
return self.tokens[self.pos]
|
return self.tokens[self.pos]
|
||||||
return Token('EOF', None)
|
return Token('EOF', None)
|
||||||
|
|
||||||
|
def consume(self, token_type):
|
||||||
|
if self.get_current_token().type == token_type:
|
||||||
|
self.pos += 1
|
||||||
|
else:
|
||||||
|
raise Exception(f"Expected {token_type}, got {self.get_current_token().type}")
|
||||||
|
|
||||||
def parse(self):
|
def parse(self):
|
||||||
statements = []
|
statements = []
|
||||||
while self.peek_next_token().type != 'EOF':
|
while self.get_current_token().type != 'EOF':
|
||||||
statements.append(self.parse_statement())
|
statements.append(self.parse_statement())
|
||||||
return statements
|
return statements
|
||||||
|
|
||||||
def parse_statement(self):
|
def parse_statement(self):
|
||||||
token = self.peek_next_token()
|
token = self.get_current_token()
|
||||||
|
|
||||||
if token.type == 'PRINT':
|
if token.type == 'PRINT':
|
||||||
return self.parse_print_statement()
|
return self.parse_print_statement()
|
||||||
@@ -120,7 +137,8 @@ class Parser:
|
|||||||
# Check for function call as a statement
|
# Check for function call as a statement
|
||||||
if self.pos + 1 < len(self.tokens) and self.tokens[self.pos + 1].type == 'LPAREN':
|
if self.pos + 1 < len(self.tokens) and self.tokens[self.pos + 1].type == 'LPAREN':
|
||||||
# Consume the ID token first, then parse the function call
|
# Consume the ID token first, then parse the function call
|
||||||
name_token = self.get_next_token()
|
name_token = self.get_current_token()
|
||||||
|
self.consume('ID') # Consume the ID token
|
||||||
return self.parse_function_call(name_token)
|
return self.parse_function_call(name_token)
|
||||||
|
|
||||||
if token.type == 'FEMBOY_FEMININE':
|
if token.type == 'FEMBOY_FEMININE':
|
||||||
@@ -138,154 +156,172 @@ class Parser:
|
|||||||
raise Exception(f"Invalid statement starting with token {token.type}")
|
raise Exception(f"Invalid statement starting with token {token.type}")
|
||||||
|
|
||||||
def parse_print_statement(self):
|
def parse_print_statement(self):
|
||||||
self.get_next_token() # Consume PRINT token
|
self.consume('PRINT')
|
||||||
expr = self.expression()
|
expr = self.expression()
|
||||||
return Print(expr)
|
return Print(expr)
|
||||||
|
|
||||||
def parse_assignment_statement(self):
|
def parse_assignment_statement(self):
|
||||||
var_token = self.get_next_token()
|
var_token = self.get_current_token()
|
||||||
|
self.consume('ID')
|
||||||
var_node = Variable(var_token)
|
var_node = Variable(var_token)
|
||||||
|
|
||||||
assign_token = self.get_next_token()
|
self.consume('ASSIGN')
|
||||||
|
|
||||||
right_expr = self.expression()
|
right_expr = self.expression()
|
||||||
|
assign_token = self.tokens[self.pos - 1] # Get the consumed ASSIGN token
|
||||||
return Assign(left=var_node, op=assign_token, right=right_expr)
|
return Assign(left=var_node, op=assign_token, right=right_expr)
|
||||||
|
|
||||||
def parse_if_statement(self):
|
def parse_if_statement(self):
|
||||||
self.get_next_token() # Consume FEMBOY_FEMININE
|
self.consume('FEMBOY_FEMININE')
|
||||||
|
|
||||||
condition = self.expression()
|
condition = self.expression()
|
||||||
|
|
||||||
# Expect Femboycore to start the if block
|
self.consume('FEMBOYCORE')
|
||||||
if self.peek_next_token().type != 'FEMBOYCORE':
|
|
||||||
raise Exception("Expected 'Femboycore' to start if block")
|
|
||||||
self.get_next_token() # Consume FEMBOYCORE
|
|
||||||
|
|
||||||
if_block_statements = []
|
if_block_statements = []
|
||||||
while self.peek_next_token().type != 'PERIODT':
|
while self.get_current_token().type != 'PERIODT':
|
||||||
if self.peek_next_token().type == 'EOF':
|
if self.get_current_token().type == 'EOF':
|
||||||
raise Exception("Unterminated if block: Expected 'Periodt'")
|
raise Exception("Unterminated if block: Expected 'Periodt'")
|
||||||
if_block_statements.append(self.parse_statement())
|
if_block_statements.append(self.parse_statement())
|
||||||
self.get_next_token() # Consume PERIODT
|
self.consume('PERIODT')
|
||||||
if_block = Block(if_block_statements)
|
if_block = Block(if_block_statements)
|
||||||
|
|
||||||
else_block = None
|
else_block = None
|
||||||
if self.peek_next_token().type == 'ANDROGYNY':
|
if self.get_current_token().type == 'ANDROGYNY':
|
||||||
self.get_next_token() # Consume ANDROGYNY
|
self.consume('ANDROGYNY')
|
||||||
# Expect Femboycore to start the else block
|
self.consume('FEMBOYCORE')
|
||||||
if self.peek_next_token().type != 'FEMBOYCORE':
|
|
||||||
raise Exception("Expected 'Femboycore' to start else block")
|
|
||||||
self.get_next_token() # Consume FEMBOYCORE
|
|
||||||
|
|
||||||
else_block_statements = []
|
else_block_statements = []
|
||||||
while self.peek_next_token().type != 'PERIODT':
|
while self.get_current_token().type != 'PERIODT':
|
||||||
if self.peek_next_token().type == 'EOF':
|
if self.get_current_token().type == 'EOF':
|
||||||
raise Exception("Unterminated else block: Expected 'Periodt'")
|
raise Exception("Unterminated else block: Expected 'Periodt'")
|
||||||
else_block_statements.append(self.parse_statement())
|
else_block_statements.append(self.parse_statement())
|
||||||
self.get_next_token() # Consume PERIODT
|
self.consume('PERIODT')
|
||||||
else_block = Block(else_block_statements)
|
else_block = Block(else_block_statements)
|
||||||
|
|
||||||
return IfStatement(condition, if_block, else_block)
|
return IfStatement(condition, if_block, else_block)
|
||||||
|
|
||||||
def parse_while_statement(self):
|
def parse_while_statement(self):
|
||||||
self.get_next_token() # Consume OTOKONOKO
|
self.consume('OTOKONOKO')
|
||||||
|
|
||||||
condition = self.expression()
|
condition = self.expression()
|
||||||
|
|
||||||
if self.peek_next_token().type != 'FEMBOYCORE':
|
self.consume('FEMBOYCORE')
|
||||||
raise Exception("Expected 'Femboycore' to start while loop body")
|
|
||||||
self.get_next_token() # Consume FEMBOYCORE
|
|
||||||
|
|
||||||
body_statements = []
|
body_statements = []
|
||||||
while self.peek_next_token().type != 'PERIODT':
|
while self.get_current_token().type != 'PERIODT':
|
||||||
if self.peek_next_token().type == 'EOF':
|
if self.get_current_token().type == 'EOF':
|
||||||
raise Exception("Unterminated while loop: Expected 'Periodt'")
|
raise Exception("Unterminated while loop: Expected 'Periodt'")
|
||||||
body_statements.append(self.parse_statement())
|
body_statements.append(self.parse_statement())
|
||||||
self.get_next_token() # Consume PERIODT
|
self.consume('PERIODT')
|
||||||
body = Block(body_statements)
|
body = Block(body_statements)
|
||||||
|
|
||||||
return WhileStatement(condition, body)
|
return WhileStatement(condition, body)
|
||||||
|
|
||||||
def parse_function_definition(self):
|
def parse_function_definition(self):
|
||||||
self.get_next_token() # Consume FUNCTION_DEF
|
self.consume('FUNCTION_DEF')
|
||||||
name_token = self.get_next_token()
|
name_token = self.get_current_token()
|
||||||
if name_token.type != 'ID':
|
self.consume('ID')
|
||||||
raise Exception("Expected function name (ID)")
|
|
||||||
|
|
||||||
# Parse parameters
|
# Parse parameters
|
||||||
parameters = []
|
parameters = []
|
||||||
if self.peek_next_token().type == 'LPAREN':
|
if self.get_current_token().type == 'LPAREN':
|
||||||
self.get_next_token() # Consume '('
|
self.consume('LPAREN')
|
||||||
while self.peek_next_token().type != 'RPAREN':
|
while self.get_current_token().type != 'RPAREN':
|
||||||
param_token = self.get_next_token()
|
param_token = self.get_current_token()
|
||||||
if param_token.type != 'ID':
|
self.consume('ID')
|
||||||
raise Exception("Expected parameter name (ID)")
|
|
||||||
parameters.append(param_token.value)
|
parameters.append(param_token.value)
|
||||||
if self.peek_next_token().type == 'COMMA':
|
if self.get_current_token().type == 'COMMA':
|
||||||
self.get_next_token() # Consume ','
|
self.consume('COMMA')
|
||||||
self.get_next_token() # Consume ')'
|
self.consume('RPAREN')
|
||||||
|
|
||||||
|
|
||||||
if self.peek_next_token().type != 'FEMBOYCORE':
|
self.consume('FEMBOYCORE')
|
||||||
raise Exception("Expected 'Femboycore' to start function body")
|
|
||||||
self.get_next_token() # Consume FEMBOYCORE
|
|
||||||
|
|
||||||
body_statements = []
|
body_statements = []
|
||||||
while self.peek_next_token().type != 'PERIODT':
|
while self.get_current_token().type != 'PERIODT':
|
||||||
if self.peek_next_token().type == 'EOF':
|
if self.get_current_token().type == 'EOF':
|
||||||
raise Exception("Unterminated function definition: Expected 'Periodt'")
|
raise Exception("Unterminated function definition: Expected 'Periodt'")
|
||||||
body_statements.append(self.parse_statement())
|
body_statements.append(self.parse_statement())
|
||||||
self.get_next_token() # Consume PERIODT
|
self.consume('PERIODT')
|
||||||
body = Block(body_statements)
|
body = Block(body_statements)
|
||||||
|
|
||||||
return FunctionDefinition(name_token.value, parameters, body)
|
return FunctionDefinition(name_token.value, parameters, body)
|
||||||
|
|
||||||
def parse_return_statement(self):
|
def parse_return_statement(self):
|
||||||
self.get_next_token() # Consume RETURN
|
self.consume('RETURN')
|
||||||
value = self.expression()
|
value = self.expression()
|
||||||
return ReturnStatement(value)
|
return ReturnStatement(value)
|
||||||
|
|
||||||
def factor(self):
|
def factor(self):
|
||||||
token = self.get_next_token()
|
token = self.get_current_token()
|
||||||
if token.type == 'INTEGER':
|
if token.type == 'INTEGER':
|
||||||
|
self.consume('INTEGER')
|
||||||
return Number(token)
|
return Number(token)
|
||||||
elif token.type == 'STRING':
|
elif token.type == 'STRING':
|
||||||
return String(token) # Now returns a String AST node
|
self.consume('STRING')
|
||||||
|
return String(token)
|
||||||
elif token.type == 'KAWAII' or token.type == 'CRINGE':
|
elif token.type == 'KAWAII' or token.type == 'CRINGE':
|
||||||
|
self.consume(token.type)
|
||||||
return Boolean(token)
|
return Boolean(token)
|
||||||
elif token.type == 'ID':
|
elif token.type == 'ID':
|
||||||
# Check for function call
|
# Consume the ID token first
|
||||||
if self.peek_next_token().type == 'LPAREN': # Assuming '(' is the next token for a function call
|
self.consume('ID')
|
||||||
|
# Now check what follows the ID
|
||||||
|
next_token = self.get_current_token()
|
||||||
|
if next_token.type == 'LPAREN':
|
||||||
|
# It's a function call
|
||||||
return self.parse_function_call(token)
|
return self.parse_function_call(token)
|
||||||
|
elif next_token.type == 'DOT':
|
||||||
|
# It's a property access
|
||||||
|
return self.parse_property_access(Variable(token)) # Pass Variable node as target
|
||||||
|
elif next_token.type == 'LBRACKET':
|
||||||
|
# It's an index access
|
||||||
|
return self.parse_index_access(Variable(token))
|
||||||
|
else:
|
||||||
|
# It's a simple variable
|
||||||
return Variable(token)
|
return Variable(token)
|
||||||
elif token.type == 'LPAREN': # Handle parenthesized expressions
|
elif token.type == 'LPAREN': # Handle parenthesized expressions
|
||||||
|
self.consume('LPAREN')
|
||||||
node = self.expression()
|
node = self.expression()
|
||||||
if self.get_next_token().type != 'RPAREN':
|
self.consume('RPAREN')
|
||||||
raise Exception("Expected ')'")
|
|
||||||
return node
|
return node
|
||||||
elif token.type == 'NOT': # Handle NOT operator
|
elif token.type == 'LBRACKET': # Handle list literals
|
||||||
return UnaryOp(token, self.factor()) # NOT applies to the next factor/expression
|
return self.parse_list_literal()
|
||||||
|
elif token.type == 'LBRACE': # Handle dictionary literals
|
||||||
|
return self.parse_dictionary_literal()
|
||||||
else:
|
else:
|
||||||
raise Exception(f"Expected integer, string, boolean or identifier, got {token.type}")
|
raise Exception(f"Expected integer, string, boolean, identifier, or literal, got {token.type}")
|
||||||
|
|
||||||
|
def unary_expression(self):
|
||||||
|
token = self.get_current_token()
|
||||||
|
if token.type == 'NOT':
|
||||||
|
self.consume('NOT')
|
||||||
|
right_node = self.unary_expression() # NOT applies to the next unary_expression
|
||||||
|
return UnaryOp(token, right_node)
|
||||||
|
return self.factor()
|
||||||
|
|
||||||
def term(self):
|
def term(self):
|
||||||
node = self.factor()
|
node = self.unary_expression()
|
||||||
while self.peek_next_token().type in ('MUL', 'DIV'):
|
while self.get_current_token().type in ('MUL', 'DIV'):
|
||||||
token = self.get_next_token()
|
token = self.get_current_token()
|
||||||
node = BinOp(left=node, op=token, right=self.factor())
|
self.consume(token.type)
|
||||||
|
node = BinOp(left=node, op=token, right=self.unary_expression())
|
||||||
return node
|
return node
|
||||||
|
|
||||||
def comparison_expression(self):
|
def comparison_expression(self):
|
||||||
node = self.term()
|
node = self.term()
|
||||||
|
|
||||||
# Handle addition/subtraction
|
# Handle addition/subtraction
|
||||||
while self.peek_next_token().type in ('PLUS', 'MINUS'):
|
while self.get_current_token().type in ('PLUS', 'MINUS'):
|
||||||
token = self.get_next_token()
|
token = self.get_current_token()
|
||||||
|
self.consume(token.type)
|
||||||
node = BinOp(left=node, op=token, right=self.term())
|
node = BinOp(left=node, op=token, right=self.term())
|
||||||
|
|
||||||
# Handle comparisons
|
# Handle comparisons
|
||||||
if self.peek_next_token().type in ('EQ', 'NEQ', 'GT', 'GTE', 'LT', 'LTE'):
|
if self.get_current_token().type in ('EQ', 'NEQ', 'GT', 'GTE', 'LT', 'LTE'):
|
||||||
op_token = self.get_next_token()
|
op_token = self.get_current_token()
|
||||||
|
self.consume(op_token.type)
|
||||||
right_node = self.comparison_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)
|
||||||
|
|
||||||
@@ -293,21 +329,81 @@ class Parser:
|
|||||||
|
|
||||||
def expression(self):
|
def expression(self):
|
||||||
node = self.comparison_expression()
|
node = self.comparison_expression()
|
||||||
while self.peek_next_token().type in ('AND', 'OR'):
|
while self.get_current_token().type in ('AND', 'OR'):
|
||||||
op_token = self.get_next_token()
|
op_token = self.get_current_token()
|
||||||
|
self.consume(op_token.type)
|
||||||
right_node = self.comparison_expression()
|
right_node = self.comparison_expression()
|
||||||
node = LogicalOp(left=node, op=op_token, right=right_node)
|
node = LogicalOp(left=node, op=op_token, right=right_node)
|
||||||
return node
|
return node
|
||||||
|
|
||||||
def parse_function_call(self, name_token):
|
def parse_function_call(self, name_token):
|
||||||
self.get_next_token() # Consume '('
|
self.consume('LPAREN')
|
||||||
arguments = []
|
arguments = []
|
||||||
if self.peek_next_token().type != 'RPAREN':
|
if self.get_current_token().type != 'RPAREN':
|
||||||
while True:
|
while True:
|
||||||
arguments.append(self.expression())
|
arguments.append(self.expression())
|
||||||
if self.peek_next_token().type == 'COMMA':
|
if self.get_current_token().type == 'COMMA':
|
||||||
self.get_next_token() # Consume ','
|
self.consume('COMMA')
|
||||||
else:
|
else:
|
||||||
break # Exit loop if not COMMA (implies RPAREN or EOF)
|
break
|
||||||
self.get_next_token() # Consume ')'
|
self.consume('RPAREN')
|
||||||
return FunctionCall(name_token.value, arguments)
|
return FunctionCall(name_token.value, arguments)
|
||||||
|
|
||||||
|
def parse_list_literal(self):
|
||||||
|
self.consume('LBRACKET')
|
||||||
|
elements = []
|
||||||
|
# Check if the list is empty
|
||||||
|
if self.get_current_token().type == 'RBRACKET':
|
||||||
|
self.consume('RBRACKET')
|
||||||
|
return List(elements)
|
||||||
|
|
||||||
|
# Parse first element
|
||||||
|
elements.append(self.expression())
|
||||||
|
|
||||||
|
# Parse subsequent elements
|
||||||
|
while self.get_current_token().type == 'COMMA':
|
||||||
|
self.consume('COMMA')
|
||||||
|
elements.append(self.expression())
|
||||||
|
|
||||||
|
# Expect closing bracket
|
||||||
|
self.consume('RBRACKET')
|
||||||
|
|
||||||
|
return List(elements)
|
||||||
|
|
||||||
|
def parse_dictionary_literal(self):
|
||||||
|
self.consume('LBRACE')
|
||||||
|
pairs = []
|
||||||
|
# Check if the dictionary is empty
|
||||||
|
if self.get_current_token().type == 'RBRACE':
|
||||||
|
self.consume('RBRACE')
|
||||||
|
return Dictionary(pairs)
|
||||||
|
|
||||||
|
# Parse first key-value pair
|
||||||
|
key = self.expression()
|
||||||
|
self.consume('COLON')
|
||||||
|
value = self.expression()
|
||||||
|
pairs.append((key, value))
|
||||||
|
|
||||||
|
# Parse subsequent key-value pairs
|
||||||
|
while self.get_current_token().type == 'COMMA':
|
||||||
|
self.consume('COMMA')
|
||||||
|
key = self.expression()
|
||||||
|
self.consume('COLON')
|
||||||
|
value = self.expression()
|
||||||
|
pairs.append((key, value))
|
||||||
|
|
||||||
|
# Expect closing brace
|
||||||
|
self.consume('RBRACE')
|
||||||
|
return Dictionary(pairs)
|
||||||
|
|
||||||
|
def parse_index_access(self, target_node):
|
||||||
|
self.consume('LBRACKET')
|
||||||
|
index_node = self.expression()
|
||||||
|
self.consume('RBRACKET')
|
||||||
|
return IndexAccess(target_node, index_node)
|
||||||
|
|
||||||
|
def parse_property_access(self, target_node):
|
||||||
|
self.consume('DOT') # Assuming DOT token for property access
|
||||||
|
property_name_token = self.get_current_token()
|
||||||
|
self.consume('ID')
|
||||||
|
return PropertyAccess(target_node, property_name_token.value)
|
||||||
Reference in New Issue
Block a user