Skip to content

Commit f95a517

Browse files
Correct errors
1 parent b4143b8 commit f95a517

2 files changed

Lines changed: 139 additions & 8 deletions

File tree

multilingualprogramming/core/semantic_lowering.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1028,7 +1028,7 @@ def _lower_agent_call(self, name: str, node: ast.CallExpr) -> object:
10281028
"""Lift memory/delegate calls to agent-coordination IR nodes."""
10291029
name = _AGENT_CANONICAL.get(name, name)
10301030
args = node.args or []
1031-
keywords = {k: self.lower(v) for k, v in self._lower_keywords(node.keywords)}
1031+
keywords = dict(self._lower_keywords(node.keywords))
10321032
ln, col = node.line, node.column
10331033
if name == "memory":
10341034
name_arg = self.lower(args[0]) if args else None

multilingualprogramming/parser/parser.py

Lines changed: 138 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@
9393
"TRACE",
9494
"COST",
9595
"EXPLAIN",
96+
"LOCAL",
9697
"EDGE",
9798
"CLOUD",
9899
"MEMORY",
@@ -657,8 +658,15 @@ def _parse_observe_declaration(self):
657658
if self._match_delimiter(":"):
658659
self._advance()
659660
annotation = self._parse_annotation_expression()
660-
self._expect_operator("=")
661-
value = self._parse_expression()
661+
if self._match_operator("="):
662+
self._advance()
663+
value = self._parse_expression()
664+
else:
665+
value = Identifier(
666+
name_tok.value,
667+
line=name_tok.line,
668+
column=name_tok.column,
669+
)
662670
return ObserveDeclaration(
663671
name_tok.value, value, annotation,
664672
line=tok.line, column=tok.column,
@@ -672,12 +680,21 @@ def _parse_on_change_statement(self):
672680
return OnChangeStatement(signal, body, line=tok.line, column=tok.column)
673681

674682
def _parse_canvas_block(self):
675-
"""Parse: canvas [name] : block."""
683+
"""Parse: canvas [name] : block or canvas [name] { ... }."""
676684
tok = self._advance() # consume CANVAS
677685
name = ""
678686
if self._match_type(TokenType.IDENTIFIER):
679687
name = self._advance().value
680-
body = self._parse_block()
688+
if self._match_delimiter("{"):
689+
self._advance()
690+
self._skip_bracket_newlines()
691+
body = []
692+
while not self._at_end() and not self._match_delimiter("}"):
693+
body.append(self._parse_statement())
694+
self._skip_bracket_newlines()
695+
self._expect_delimiter("}")
696+
else:
697+
body = self._parse_block()
681698
return CanvasBlock(name=name, body=body, line=tok.line, column=tok.column)
682699

683700
def _parse_render_statement(self):
@@ -921,6 +938,32 @@ def _parse_case_pattern(self):
921938
"""
922939
pattern = self._parse_or_expression()
923940

941+
if self._match_delimiter("{") and isinstance(pattern, Identifier):
942+
entries = []
943+
self._advance()
944+
self._skip_bracket_newlines()
945+
while not self._match_delimiter("}") and not self._at_end():
946+
key_tok = self._expect_identifier()
947+
key = Identifier(
948+
key_tok.value,
949+
line=key_tok.line,
950+
column=key_tok.column,
951+
)
952+
self._expect_delimiter(":")
953+
value = self._parse_expression()
954+
entries.append((key, value))
955+
if self._match_delimiter(","):
956+
self._advance()
957+
self._skip_bracket_newlines()
958+
self._expect_delimiter("}")
959+
pattern = BinaryOp(
960+
pattern,
961+
"{}",
962+
DictLiteral(entries, line=pattern.line, column=pattern.column),
963+
line=pattern.line,
964+
column=pattern.column,
965+
)
966+
924967
# OR patterns: pattern | pattern | ...
925968
if self._match_operator("|"):
926969
patterns = [pattern]
@@ -1242,6 +1285,14 @@ def _parse_return_statement(self):
12421285
and not self._match_type(TokenType.DEDENT) \
12431286
and not self._match_type(TokenType.EOF):
12441287
value = self._parse_expression()
1288+
if self._match_delimiter(","):
1289+
elements = [value]
1290+
while self._match_delimiter(","):
1291+
self._advance()
1292+
if self._at_end() or self._match_type(TokenType.NEWLINE):
1293+
break
1294+
elements.append(self._parse_expression())
1295+
value = TupleLiteral(elements, line=tok.line, column=tok.column)
12451296
return ReturnStatement(value, line=tok.line, column=tok.column)
12461297

12471298
def _parse_yield_statement(self):
@@ -1724,6 +1775,10 @@ def _parse_keyword_atom(self, tok):
17241775
if concept == "NONE":
17251776
self._advance()
17261777
return NoneLiteral(line=tok.line, column=tok.column)
1778+
if concept == "PAR" and self._match_next_delimiter("["):
1779+
return self._parse_prefix_soft_call()
1780+
if concept == "SPAWN" and self._has_inline_expression_after_keyword():
1781+
return self._parse_prefix_soft_call()
17271782
if concept in _AI_NATIVE_CONCEPTS and self._is_native_ai_form():
17281783
return self._parse_native_ai_expression()
17291784
if concept in _IDENTIFIER_LIKE_CONCEPTS:
@@ -1735,6 +1790,33 @@ def _parse_keyword_atom(self, tok):
17351790
return self._parse_yield_expr()
17361791
return None
17371792

1793+
def _match_next_delimiter(self, delim):
1794+
"""Check whether the next token is a specific delimiter."""
1795+
idx = self.pos + 1
1796+
if idx < len(self.tokens):
1797+
tok = self.tokens[idx]
1798+
return tok.type == TokenType.DELIMITER and tok.value == delim
1799+
return False
1800+
1801+
def _has_inline_expression_after_keyword(self):
1802+
"""Return True when the next token starts an inline expression."""
1803+
idx = self.pos + 1
1804+
if idx >= len(self.tokens):
1805+
return False
1806+
nxt = self.tokens[idx]
1807+
return nxt.type not in {
1808+
TokenType.NEWLINE,
1809+
TokenType.DEDENT,
1810+
TokenType.EOF,
1811+
}
1812+
1813+
def _parse_prefix_soft_call(self):
1814+
"""Parse soft-keyword prefix forms such as `par [..]` or `spawn expr`."""
1815+
tok = self._advance()
1816+
func = Identifier(tok.value, line=tok.line, column=tok.column)
1817+
arg = self._parse_expression()
1818+
return CallExpr(func, [arg], [], line=tok.line, column=tok.column)
1819+
17381820
def _parse_atom(self): # pylint: disable=too-many-branches
17391821
"""Parse atomic expressions: literals, identifiers, parenthesized."""
17401822
tok = self._current()
@@ -1858,7 +1940,7 @@ def _parse_native_ai_expression(self):
18581940

18591941
if self._match_delimiter(":"):
18601942
self._advance()
1861-
args.append(self._parse_expression())
1943+
args.append(self._parse_native_ai_template(tok))
18621944
else:
18631945
self._error("EXPECTED_EXPRESSION", self._current(), token=self._current().value)
18641946

@@ -1871,6 +1953,42 @@ def _parse_native_ai_expression(self):
18711953

18721954
return node
18731955

1956+
def _parse_native_ai_template(self, tok):
1957+
"""Parse the template section of a native AI expression."""
1958+
if not self._match_type(TokenType.NEWLINE):
1959+
return self._parse_expression()
1960+
1961+
self._advance()
1962+
self._skip_newlines()
1963+
if not self._match_type(TokenType.INDENT):
1964+
return StringLiteral("", line=tok.line, column=tok.column)
1965+
1966+
self._advance()
1967+
lines = []
1968+
current_line = []
1969+
1970+
while not self._at_end() and not self._match_type(TokenType.DEDENT):
1971+
cur = self._current()
1972+
if cur.type == TokenType.NEWLINE:
1973+
if current_line:
1974+
lines.append(" ".join(current_line).strip())
1975+
current_line = []
1976+
self._advance()
1977+
continue
1978+
current_line.append(str(cur.value))
1979+
self._advance()
1980+
1981+
if current_line:
1982+
lines.append(" ".join(current_line).strip())
1983+
if self._match_type(TokenType.DEDENT):
1984+
self._advance()
1985+
1986+
return StringLiteral(
1987+
"\n".join(line for line in lines if line),
1988+
line=tok.line,
1989+
column=tok.column,
1990+
)
1991+
18741992
def _parse_model_ref_literal(self):
18751993
"""Parse a model reference literal starting with `@`."""
18761994
tok = self._advance() # consume @
@@ -2009,7 +2127,7 @@ def _parse_list_literal(self):
20092127
self._advance() # consume ]
20102128
return ListLiteral([], line=tok.line, column=tok.column)
20112129

2012-
first = self._parse_expression()
2130+
first = self._parse_list_element()
20132131

20142132
# Check for list comprehension: [expr FOR ...]
20152133
if self._match_concept("LOOP_FOR"):
@@ -2023,11 +2141,24 @@ def _parse_list_literal(self):
20232141
self._skip_bracket_newlines()
20242142
if self._match_delimiter("]"):
20252143
break
2026-
elements.append(self._parse_expression())
2144+
elements.append(self._parse_list_element())
20272145
self._skip_bracket_newlines()
20282146
self._expect_delimiter("]")
20292147
return ListLiteral(elements, line=tok.line, column=tok.column)
20302148

2149+
def _parse_list_element(self):
2150+
"""Parse a single list element, including starred forms."""
2151+
if self._match_operator("*"):
2152+
tok = self._advance()
2153+
value = self._parse_expression()
2154+
return StarredExpr(
2155+
value,
2156+
is_double=False,
2157+
line=tok.line,
2158+
column=tok.column,
2159+
)
2160+
return self._parse_expression()
2161+
20312162
def _parse_brace_literal(self):
20322163
"""Parse dict or set literal, including dict unpacking."""
20332164
tok = self._advance() # consume {

0 commit comments

Comments
 (0)