Skip to content

Commit fe496d9

Browse files
committed
grammar, parser, printers
1 parent e965e6f commit fe496d9

13 files changed

Lines changed: 12957 additions & 11582 deletions

File tree

meta/src/meta/codegen_templates.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class BuiltinTemplate:
3434
"make_empty_bytes": BuiltinTemplate('b""'),
3535
"dict_from_list": BuiltinTemplate("dict({0})"),
3636
"dict_get": BuiltinTemplate("{0}.get({1})"),
37+
"dict_to_pairs": BuiltinTemplate("sorted({0}.items())"),
3738
"has_proto_field": BuiltinTemplate("{0}.HasField({1})"),
3839
"string_to_upper": BuiltinTemplate("{0}.upper()"),
3940
"string_in_list": BuiltinTemplate("{0} in {1}"),
@@ -134,6 +135,7 @@ class BuiltinTemplate:
134135
"make_empty_bytes": BuiltinTemplate("UInt8[]"),
135136
"dict_from_list": BuiltinTemplate("Dict({0})"),
136137
"dict_get": BuiltinTemplate("get({0}, {1}, nothing)"),
138+
"dict_to_pairs": BuiltinTemplate("sort(collect(pairs({0})))"),
137139
"has_proto_field": BuiltinTemplate("_has_proto_field({0}, Symbol({1}))"),
138140
"string_to_upper": BuiltinTemplate("uppercase({0})"),
139141
"string_in_list": BuiltinTemplate("({0} in {1})"),
@@ -231,6 +233,7 @@ class BuiltinTemplate:
231233
"make_empty_bytes": BuiltinTemplate("[]byte{}"),
232234
"dict_from_list": BuiltinTemplate("dictFromList({0})"),
233235
"dict_get": BuiltinTemplate("dictGetValue({0}, {1})"),
236+
"dict_to_pairs": BuiltinTemplate("dictToPairs({0})"),
234237
"has_proto_field": BuiltinTemplate("hasProtoField({0}, {1})"),
235238
"string_to_upper": BuiltinTemplate("strings.ToUpper({0})"),
236239
"string_in_list": BuiltinTemplate("stringInList({0}, {1})"),

meta/src/meta/grammar.y

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,15 @@
8383
%nonterm csv_locator_paths Sequence[String]
8484
%nonterm csvlocator logic.CSVLocator
8585
%nonterm data logic.Data
86+
%nonterm iceberg_config logic.IcebergConfig
87+
%nonterm iceberg_config_credentials Sequence[Tuple[String, String]]
88+
%nonterm iceberg_config_properties Sequence[Tuple[String, String]]
89+
%nonterm iceberg_config_scope String
90+
%nonterm iceberg_data logic.IcebergData
91+
%nonterm iceberg_kv_pair Tuple[String, String]
92+
%nonterm iceberg_locator logic.IcebergLocator
93+
%nonterm iceberg_locator_namespace Sequence[String]
94+
%nonterm iceberg_to_snapshot String
8695
%nonterm date logic.DateValue
8796
%nonterm date_type logic.DateType
8897
%nonterm datetime logic.DateTimeValue
@@ -957,6 +966,10 @@ data
957966
construct: $$ = logic.Data(csv_data=$1)
958967
deconstruct if builtin.has_proto_field($$, 'csv_data'):
959968
$1: logic.CSVData = $$.csv_data
969+
| iceberg_data
970+
construct: $$ = logic.Data(iceberg_data=$1)
971+
deconstruct if builtin.has_proto_field($$, 'iceberg_data'):
972+
$1: logic.IcebergData = $$.iceberg_data
960973

961974
edb_path
962975
: "[" STRING* "]"
@@ -1026,6 +1039,54 @@ csv_config
10261039
construct: $$ = construct_csv_config($3)
10271040
deconstruct: $3: Sequence[Tuple[String, logic.Value]] = deconstruct_csv_config($$)
10281041

1042+
iceberg_data
1043+
: "(" "iceberg_data" iceberg_locator iceberg_config gnf_columns iceberg_to_snapshot? ")"
1044+
construct: $$ = logic.IcebergData(locator=$3, config=$4, columns=$5, to_snapshot=$6)
1045+
deconstruct:
1046+
$3: logic.IcebergLocator = $$.locator
1047+
$4: logic.IcebergConfig = $$.config
1048+
$5: Sequence[logic.GNFColumn] = $$.columns
1049+
$6: Optional[String] = $$.to_snapshot
1050+
1051+
iceberg_locator_namespace
1052+
: "(" "namespace" STRING* ")"
1053+
1054+
iceberg_locator
1055+
: "(" "iceberg_locator" STRING iceberg_locator_namespace STRING ")"
1056+
construct: $$ = logic.IcebergLocator(table_name=$3, namespace=$4, warehouse=$5)
1057+
deconstruct:
1058+
$3: String = $$.table_name
1059+
$4: Sequence[String] = $$.namespace
1060+
$5: String = $$.warehouse
1061+
1062+
iceberg_config
1063+
: "(" "iceberg_config" STRING iceberg_config_scope? iceberg_config_properties? iceberg_config_credentials? ")"
1064+
construct: $$ = construct_iceberg_config($3, $4, $5, $6)
1065+
deconstruct:
1066+
$3: String = $$.catalog_uri
1067+
$4: Optional[String] = $$.scope if $$.scope != "" else None
1068+
$5: Optional[Sequence[Tuple[String, String]]] = builtin.dict_to_pairs($$.properties) if not builtin.is_empty(builtin.dict_to_pairs($$.properties)) else None
1069+
$6: Optional[Sequence[Tuple[String, String]]] = builtin.dict_to_pairs($$.credentials) if not builtin.is_empty(builtin.dict_to_pairs($$.credentials)) else None
1070+
1071+
iceberg_config_scope
1072+
: "(" "scope" STRING ")"
1073+
1074+
iceberg_config_properties
1075+
: "(" "properties" iceberg_kv_pair* ")"
1076+
1077+
iceberg_config_credentials
1078+
: "(" "credentials" iceberg_kv_pair* ")"
1079+
1080+
iceberg_kv_pair
1081+
: "(" STRING STRING ")"
1082+
construct: $$ = builtin.tuple($2, $3)
1083+
deconstruct:
1084+
$2: String = $$[0]
1085+
$3: String = $$[1]
1086+
1087+
iceberg_to_snapshot
1088+
: "(" "to_snapshot" STRING ")"
1089+
10291090
gnf_column_path
10301091
: STRING
10311092
construct: $$ = [$1]
@@ -1439,6 +1500,23 @@ def deconstruct_csv_config(msg: logic.CSVConfig) -> List[Tuple[String, logic.Val
14391500
return builtin.list_sort(result)
14401501

14411502

1503+
def construct_iceberg_config(
1504+
catalog_uri: String,
1505+
scope: Optional[String],
1506+
properties: Optional[Sequence[Tuple[String, String]]],
1507+
credentials: Optional[Sequence[Tuple[String, String]]],
1508+
) -> logic.IcebergConfig:
1509+
props: Dict[String, String] = builtin.dict_from_list(builtin.unwrap_option_or(properties, list[Tuple[String, String]]()))
1510+
creds: Dict[String, String] = builtin.dict_from_list(builtin.unwrap_option_or(credentials, list[Tuple[String, String]]()))
1511+
return logic.IcebergConfig(
1512+
catalog_uri=catalog_uri,
1513+
scope=builtin.unwrap_option_or(scope, ""),
1514+
properties=props,
1515+
credentials=creds,
1516+
)
1517+
1518+
1519+
14421520

14431521
def deconstruct_betree_info_config(msg: logic.BeTreeInfo) -> List[Tuple[String, logic.Value]]:
14441522
result: List[Tuple[String, logic.Value]] = list[Tuple[String, logic.Value]]()

meta/src/meta/proto_ast.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ class ProtoField:
5656
number: int
5757
is_repeated: bool = False
5858
is_optional: bool = False
59+
is_map: bool = False
60+
map_key_type: str = ""
61+
map_value_type: str = ""
5962

6063

6164
@dataclass

meta/src/meta/proto_parser.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
_NESTED_ENUM_PATTERN = re.compile(r"enum\s+(\w+)\s*\{([^}]+)\}")
4141
_ONEOF_PATTERN = re.compile(r"oneof\s+(\w+)\s*\{((?:[^{}]|\{[^}]*\})*)\}")
4242
_FIELD_PATTERN = re.compile(r"(repeated|optional)?\s*(\w+)\s+(\w+)\s*=\s*(\d+);")
43+
_MAP_FIELD_PATTERN = re.compile(r"map<\s*(\w+)\s*,\s*(\w+)\s*>\s+(\w+)\s*=\s*(\d+);")
4344
_ONEOF_FIELD_PATTERN = re.compile(r"(\w+)\s+(\w+)\s*=\s*(\d+);")
4445
_ENUM_VALUE_PATTERN = re.compile(r"(\w+)\s*=\s*(\d+);")
4546
_RESERVED_PATTERN = re.compile(r"reserved\s+([^;]+);")
@@ -193,6 +194,29 @@ def _parse_message(self, name: str, body: str) -> ProtoMessage:
193194
)
194195
message.fields.append(proto_field)
195196

197+
# Parse map fields
198+
for match in _MAP_FIELD_PATTERN.finditer(body):
199+
if any(
200+
start <= match.start() and match.end() <= end
201+
for start, end in excluded_spans
202+
):
203+
continue
204+
205+
key_type = match.group(1)
206+
value_type = match.group(2)
207+
field_name = match.group(3)
208+
field_number = int(match.group(4))
209+
210+
proto_field = ProtoField(
211+
name=field_name,
212+
type=f"map<{key_type},{value_type}>",
213+
number=field_number,
214+
is_map=True,
215+
map_key_type=key_type,
216+
map_value_type=value_type,
217+
)
218+
message.fields.append(proto_field)
219+
196220
# Parse nested enums
197221
for match in _NESTED_ENUM_PATTERN.finditer(body):
198222
enum_name = match.group(1)

meta/src/meta/target_builtins.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ def is_builtin(name: str) -> bool:
218218
# === Dict operations ===
219219
register_builtin("dict_from_list", [SequenceType(TupleType([K, V]))], DictType(K, V))
220220
register_builtin("dict_get", [DictType(K, V), K], OptionType(V))
221+
register_builtin("dict_to_pairs", [DictType(K, V)], ListType(TupleType([K, V])))
221222

222223
# === Protobuf operations ===
223224
register_builtin("has_proto_field", [T, STRING], BOOLEAN) # msg.HasField(field_name)

meta/src/meta/type_env.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from .proto_parser import ProtoParser
55
from .target import (
66
BaseType,
7+
DictType,
78
FunctionType,
89
MessageType,
910
OptionType,
@@ -27,6 +28,13 @@
2728
}
2829

2930

31+
def _scalar_to_target(type_name: str) -> TargetType:
32+
"""Convert a scalar proto type name to a TargetType."""
33+
if type_name in _PRIMITIVE_TO_BASE_TYPE:
34+
return BaseType(_PRIMITIVE_TO_BASE_TYPE[type_name])
35+
raise ValueError(f"Unknown scalar proto type for map: {type_name}")
36+
37+
3038
class TypeEnv:
3139
"""Type environment for validating grammar expressions.
3240
@@ -87,6 +95,12 @@ def _is_enum_type(self, type_name: str) -> bool:
8795

8896
def _proto_type_to_target(self, proto_field: ProtoField) -> TargetType:
8997
"""Convert a protobuf field to its target type."""
98+
# Handle map fields
99+
if proto_field.is_map:
100+
key_type = _scalar_to_target(proto_field.map_key_type)
101+
value_type = _scalar_to_target(proto_field.map_value_type)
102+
return DictType(key_type, value_type)
103+
90104
# Get base type
91105
base_type: TargetType
92106
if proto_field.type in _PRIMITIVE_TO_BASE_TYPE:

meta/src/meta/yacc_parser.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
)
7171
from .target import (
7272
BaseType,
73+
DictType,
7374
ListType,
7475
MessageType,
7576
OptionType,
@@ -739,10 +740,19 @@ def _make_field_type_lookup(
739740

740741
for (module, msg_name), proto_msg in proto_messages.items():
741742
for field in proto_msg.fields:
742-
field_type = _proto_type_to_target_type(
743-
field.type, field.is_repeated, field.is_optional, name_to_module
744-
)
745-
field_types[(module, msg_name, field.name)] = field_type
743+
if field.is_map:
744+
key_type = _proto_type_to_target_type(
745+
field.map_key_type, False, name_to_module=name_to_module
746+
)
747+
value_type = _proto_type_to_target_type(
748+
field.map_value_type, False, name_to_module=name_to_module
749+
)
750+
field_types[(module, msg_name, field.name)] = DictType(key_type, value_type)
751+
else:
752+
field_type = _proto_type_to_target_type(
753+
field.type, field.is_repeated, field.is_optional, name_to_module
754+
)
755+
field_types[(module, msg_name, field.name)] = field_type
746756

747757
# Also add oneof fields
748758
for oneof in proto_msg.oneofs:

0 commit comments

Comments
 (0)