add
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
Reference in New Issue
Block a user