This commit is contained in:
lz_db
2025-11-16 12:31:03 +08:00
commit 0fab423a18
1451 changed files with 743213 additions and 0 deletions

View File

@@ -0,0 +1,77 @@
import dataclasses
from typing import List, Optional
class CairoType:
"""
Base class for cairo types.
"""
@dataclasses.dataclass
class TypeFelt(CairoType):
pass
@dataclasses.dataclass
class TypeCodeoffset(CairoType):
pass
@dataclasses.dataclass
class TypePointer(CairoType):
pointee: CairoType
@dataclasses.dataclass
class TypeIdentifier(CairoType):
"""
Represents a name of an unresolved type.
This type can be resolved to TypeStruct or TypeDefinition.
"""
name: str
@dataclasses.dataclass
class TypeStruct(CairoType):
scope: str
@dataclasses.dataclass
class TypeFunction(CairoType):
"""
Represents a type of a function.
"""
scope: str
@dataclasses.dataclass
class TypeTuple(CairoType):
"""
Represents a type of a named or unnamed tuple.
For example, "(felt, felt*)" or "(a: felt, b: felt*)".
"""
@dataclasses.dataclass
class Item(CairoType):
"""
Represents a possibly named type item of a TypeTuple.
For example: "felt" or "a: felt".
"""
name: Optional[str]
typ: CairoType
members: List["TypeTuple.Item"]
has_trailing_comma: bool = dataclasses.field(hash=False, compare=False)
@property
def is_named(self) -> bool:
return all(member.name is not None for member in self.members)
@dataclasses.dataclass
class ExprIdentifier(CairoType):
name: str

View File

@@ -0,0 +1,46 @@
from ....lark import Lark
from .cairo_types import CairoType
from .parser_transformer import ParserTransformer
CAIRO_EBNF = """
%import common.WS_INLINE
%ignore WS_INLINE
IDENTIFIER: /[a-zA-Z_][a-zA-Z_0-9]*/
_DBL_STAR: "**"
COMMA: ","
?type: non_identifier_type
| identifier -> type_struct
comma_separated{item}: item? (COMMA item)* COMMA?
named_type: identifier (":" type)? | non_identifier_type
non_identifier_type: "felt" -> type_felt
| "codeoffset" -> type_codeoffset
| type "*" -> type_pointer
| type _DBL_STAR -> type_pointer2
| "(" comma_separated{named_type} ")" -> type_tuple
identifier: IDENTIFIER ("." IDENTIFIER)*
"""
def parse(code: str) -> CairoType:
"""
Parses the given string and returns a CairoType.
"""
grammar = CAIRO_EBNF
grammar_parser = Lark(
grammar=grammar,
start=["type"],
parser="lalr",
)
parsed = grammar_parser.parse(code)
transformed = ParserTransformer().transform(parsed)
return transformed

View File

@@ -0,0 +1,138 @@
import dataclasses
from typing import Optional, Tuple
from ....lark import Token, Transformer, v_args
from .cairo_types import (
CairoType,
ExprIdentifier,
TypeCodeoffset,
TypeFelt,
TypeIdentifier,
TypePointer,
TypeStruct,
TypeTuple,
)
@dataclasses.dataclass
class ParserContext:
"""
Represents information that affects the parsing process.
"""
# If True, treat type identifiers as resolved.
resolved_types: bool = False
class ParserError(Exception):
"""
Base exception for parsing process.
"""
@dataclasses.dataclass
class CommaSeparated:
"""
Represents a list of comma separated values, such as expressions or types.
"""
args: list
has_trailing_comma: bool
class ParserTransformer(Transformer):
"""
Transforms the lark tree into an AST based on the classes defined in cairo_types.py.
"""
# pylint: disable=unused-argument, no-self-use
def __init__(self):
super().__init__()
self.parser_context = ParserContext()
def __default__(self, data: str, children, meta):
raise TypeError(f"Unable to parse tree node of type {data}")
def comma_separated(self, value) -> CommaSeparated:
saw_comma = None
args: list = []
for v in value:
if isinstance(v, Token) and v.type == "COMMA":
if saw_comma is not False:
raise ParserError("Unexpected comma.")
saw_comma = True
else:
if saw_comma is False:
raise ParserError("Expected a comma before this expression.")
args.append(v)
# Reset state.
saw_comma = False
if saw_comma is None:
saw_comma = False
return CommaSeparated(args=args, has_trailing_comma=saw_comma)
# Types.
@v_args(meta=True)
def named_type(self, meta, value) -> TypeTuple.Item:
name: Optional[str]
if len(value) == 1:
# Unnamed type.
(typ,) = value
name = None
if isinstance(typ, ExprIdentifier):
typ = self.type_struct([typ])
elif len(value) == 2:
# Named type.
identifier, typ = value
assert isinstance(identifier, ExprIdentifier)
assert isinstance(typ, CairoType)
if "." in identifier.name:
raise ParserError("Unexpected . in name.")
name = identifier.name
else:
raise NotImplementedError(f"Unexpected number of values. {value}")
return TypeTuple.Item(name=name, typ=typ)
@v_args(meta=True)
def type_felt(self, meta, value):
return TypeFelt()
@v_args(meta=True)
def type_codeoffset(self, meta, value):
return TypeCodeoffset()
def type_struct(self, value):
assert len(value) == 1 and isinstance(value[0], ExprIdentifier)
if self.parser_context.resolved_types:
# If parser_context.resolved_types is True, assume that the type is a struct.
return TypeStruct(scope=value[0].name)
return TypeIdentifier(name=value[0].name)
@v_args(meta=True)
def type_pointer(self, meta, value):
return TypePointer(pointee=value[0])
@v_args(meta=True)
def type_pointer2(self, meta, value):
return TypePointer(pointee=TypePointer(pointee=value[0]))
@v_args(meta=True)
def type_tuple(self, meta, value: Tuple[CommaSeparated]):
(lst,) = value
return TypeTuple(members=lst.args, has_trailing_comma=lst.has_trailing_comma)
@v_args(meta=True)
def identifier(self, meta, value):
return ExprIdentifier(name=".".join(x.value for x in value))
@v_args(meta=True)
def identifier_def(self, meta, value):
return ExprIdentifier(name=value[0].value)