Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ jobs:
contents: read
strategy:
matrix:
python_version: [3.12, 3.13, 3.14]
python_version: [3.11, 3.12, 3.13, 3.14]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
Expand Down
2 changes: 1 addition & 1 deletion Tools/Python/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "strata"
version = "0.0.1"
description = 'Python support for Strata.'
requires-python = ">= 3.12"
requires-python = ">= 3.11"
dependencies = [
"amazon.ion"
]
Expand Down
5 changes: 5 additions & 0 deletions Tools/Python/scripts/run_cpython_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ elif [ "$VER" == "3.12" ]; then
expected_failures="$expected_failures;/test_lib2to3/data/bom.py"
expected_failures="$expected_failures;/test_lib2to3/data/py2_test_grammar.py"
expected_failures="$expected_failures;/test_lib2to3/data/crlf.py"
elif [ "$VER" == "3.11" ]; then
expected_failures="/tokenizedata/bad_coding.py"
expected_failures="$expected_failures;/tokenizedata/bad_coding2.py"
expected_failures="$expected_failures;/tokenizedata/badsyntax_3131.py"
expected_failures="$expected_failures;/tokenizedata/badsyntax_pep3120.py"
else
expected_failures=""
fi
Expand Down
28 changes: 16 additions & 12 deletions Tools/Python/strata/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from decimal import Decimal
import sys
import typing
from typing import Any, Callable, Iterable, cast
from typing import Any, Callable, Iterable, TypeVar, Generic, Union

import amazon.ion.simpleion as ion

Expand Down Expand Up @@ -113,7 +113,8 @@ def __init__(self, name: QualifiedIdent, args: tuple['SyntaxCat', ...] | None =
self.args = () if args is None else args

def strPrec(self, prec: int) -> str:
s = f'{str(self.name)}{"".join(' ' + a.strPrec(10) for a in self.args)}'
args = ''.join(' ' + a.strPrec(10) for a in self.args)
s = f'{str(self.name)}{args}'
return f'({s})' if prec > 0 else s

def __str__(self) -> str:
Expand Down Expand Up @@ -353,12 +354,14 @@ def __init__(self, value: str, *, ann = None):
def __str__(self):
return f'StrLit({repr(self.value)})'

T = TypeVar('T', bound='Arg')

@dataclass
class OptionArg[T : 'Arg']:
value: T|None
class OptionArg(Generic[T]):
value: Union[T, None]
ann : Any

def __init__(self, value: T|None, *, ann = None):
def __init__(self, value: Union[T, None], *, ann = None):
self.value = value
self.ann = ann

Expand All @@ -372,7 +375,7 @@ def __str__(self):
return f'Some({self.value})'

@dataclass
class Seq[T : 'Arg']:
class Seq(Generic[T]):
values: tuple[T, ...]
ann : Any

Expand Down Expand Up @@ -400,8 +403,7 @@ def __init__(self, values: list['Arg'], *, ann = None):
self.values = values
self.ann = ann

type Arg = SyntaxCat | Operation | TypeExpr | Expr | Ident \
| BytesLit | NumLit | DecimalLit | StrLit | OptionArg['Arg'] | Seq['Arg'] | CommaSepBy
Arg = Union[SyntaxCat, Operation, TypeExpr, Expr, Ident, BytesLit, NumLit, DecimalLit, StrLit, OptionArg['Arg'], Seq['Arg'], CommaSepBy]

strlitSym = ion_symbol("strlit")
numSym = ion_symbol("num")
Expand Down Expand Up @@ -493,7 +495,7 @@ class MetadataAttr:
def to_ion(self):
return ion_sexp(self.ident.to_ion(), *(metadata_arg_to_ion(a) for a in self.args))

type Metadata = list[MetadataAttr]
Metadata = list[MetadataAttr]

def metadata_to_ion(values):
return [ v.to_ion() for v in values ]
Expand Down Expand Up @@ -531,7 +533,7 @@ def __init__(self, indent: int, args: tuple['SyntaxDefAtom', ...]):
def to_ion(self):
return ion_sexp(ion_symbol("indent"), self.indent, *(syntaxdef_atom_to_ion(a) for a in self.args))

type SyntaxDefAtom = SyntaxDefAtomBase | str
SyntaxDefAtom = Union[SyntaxDefAtomBase, str]

def syntaxdef_atom_to_ion(atom : SyntaxDefAtom) -> object:
if isinstance(atom, str):
Expand Down Expand Up @@ -846,7 +848,9 @@ def read_string(reader) -> str:
assert isinstance(scalar, str)
return scalar

def read_list[X](reader : object, f : Callable[[object, ion.IonEvent], X] ) -> tuple[X, ...]:
X = TypeVar('X')

def read_list(reader : object, f : Callable[[object, ion.IonEvent], X] ) -> tuple[X, ...]:
res = []
while True:
event = read_event(reader)
Expand All @@ -855,7 +859,7 @@ def read_list[X](reader : object, f : Callable[[object, ion.IonEvent], X] ) -> t
v = f(reader, event)
res.append(v)

def read_sexpr[X](reader : object, f : Callable[[object, ion.IonEvent], X] ) -> tuple[X, ...]:
def read_sexpr(reader : object, f : Callable[[object, ion.IonEvent], X] ) -> tuple[X, ...]:
res = []
while True:
event = read_event(reader)
Expand Down
23 changes: 19 additions & 4 deletions Tools/Python/strata/pythonast.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,13 @@ def populate_op(name : str, op : type) -> Op:
arg = decl.args[idx]
cat = arg.kind
assert isinstance(cat, SyntaxCat)
missing = strata.OptionArg(None)
# Default value depends on the category type
if cat.name == Init.Option.ident:
missing = strata.OptionArg(None)
elif cat.name == Init.Seq.ident:
missing = strata.Seq(())
else:
raise ValueError(f"Unexpected category type for missing field: {cat.name}")
op_args.append(OpArg(None, cat, missing=missing))

return Op(decl, op_args)
Expand All @@ -214,13 +220,21 @@ def populate_op(name : str, op : type) -> Op:
new_args : list[tuple[type, str]]
if sys.version_info >= (3, 13):
new_args = []
else:
assert sys.version_info >= (3, 12)
elif sys.version_info >= (3, 12):
new_args = [
(ast.TypeVar, "default_value"),
(ast.ParamSpec, "default_value"),
(ast.TypeVarTuple, "default_value"),
]
else:
# Python 3.11 - missing type_params added in 3.12 and type parameter nodes
new_args = [
(ast.FunctionDef, "type_params"),
(ast.AsyncFunctionDef, "type_params"),
(ast.ClassDef, "type_params"),
]
# TypeVar, ParamSpec, TypeVarTuple nodes don't exist in 3.11 but are in the 3.14 dialect
# We need to handle them separately since we can't reference non-existent AST nodes

def check_op(d : strata.Dialect, name, op : type):
opd = getattr(d, name)
Expand All @@ -238,7 +252,8 @@ def check_op(d : strata.Dialect, name, op : type):
assert (op, arg.name) in new_args, f"Extra field name {arg.name} in {opd.name}"
k = arg.kind
assert isinstance(k, SyntaxCat)
assert k.name == Init.Option.ident, f"Bad type for {op} {arg.name}"
# Extra fields can be Option (defaults to None) or Seq (defaults to empty list)
assert k.name == Init.Option.ident or k.name == Init.Seq.ident, f"Bad type for {op} {arg.name}: {k.name}"
l += 1

def check_ast(d : strata.Dialect):
Expand Down
Loading