3 Commits
Author SHA1 Message Date
HEL 9fdc003a71 chore: allow passing commit hash through inputs
Compile manual / ci (pull_request) Successful in 1m45s
2026-07-07 19:07:52 +02:00
HEL 496413415f chore: add ci to compile manual
Compile manual / ci (pull_request) Failing after 4m1s
2026-07-07 18:44:21 +02:00
HEL 7c5e657e77 chore: update README 2026-07-07 18:05:12 +02:00
35 changed files with 1830 additions and 2783 deletions
+48
View File
@@ -0,0 +1,48 @@
name: Compile manual
on:
push:
branches:
- main
- master
pull_request:
branches:
- "**"
jobs:
ci:
runs-on: ubuntu-latest
container: catthehacker/ubuntu:act-latest
if: github.event.pull_request.draft == false
steps:
- uses: actions/checkout@v3
- uses: ruby/setup-ruby@v1
with:
ruby-version: "3.2"
- name: Setup Fontist
uses: fontist/setup-fontist@v2
- name: Install Fonts
run: fontist manifest install docs/fonts.yaml
- uses: typst-community/setup-typst@v5
with:
typst-version: "0.15.0"
zip-packages: docs/requirements.json
cache-local-packages: true
token: ""
- run: |
typst compile \
--root . \
--font-path ~/.fontist/fonts \
--input hash=${{ gitea.sha }} \
docs/manual.typ \
docs/manual.pdf
- name: Upload artifact
uses: christopherhx/gitea-upload-artifact@v4
with:
name: manual
path: docs/manual.pdf
-24
View File
@@ -1,24 +0,0 @@
name: Tests
on:
push:
branches:
- main
- master
pull_request:
branches:
- "**"
jobs:
tests:
runs-on: ubuntu-latest
container: catthehacker/ubuntu:act-latest
if: github.event.pull_request.draft == false
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version-file: pyproject.toml
- name: Install uv
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
- run: python3 -m tests
+1 -3
View File
@@ -6,6 +6,4 @@ venv
*.pyc
uv.lock
.python-version
/out
/examples/**/build/
/examples/**/*.pyi
/out
@@ -5,7 +5,7 @@ type Celsius = float
type Kelvin = float where _ >= 0
type Hectopascal = float
type Temperature = Celsius where in_range(-30.0, 100.0)(_)
type Temperature = Celsius where in_range(-30.0, 100.0)
type Pressure = Hectopascal where in_range(800.0, 1100.0)(_)
type Humidity = float where is_percentage(_)
type HeatIndex = float
@@ -38,7 +38,7 @@ alias RawData = Frame[
alias Data = Frame[
station_id: StationID,
timestamp: Any,
timestamp: object,
temperature: Temperature,
pressure: Pressure,
humidity: Humidity,
@@ -46,7 +46,7 @@ alias Data = Frame[
alias DataWithHI = Frame[
station_id: StationID,
timestamp: Any,
timestamp: object,
temperature: Temperature,
pressure: Pressure,
humidity: Humidity,
@@ -54,12 +54,12 @@ alias DataWithHI = Frame[
]
alias DailyAverages = Frame[
timestamp: Any,
timestamp: object,
temperature: Mean[Temperature],
pressure: Mean[Pressure],
humidity: Mean[Humidity],
heat_index: Mean[HeatIndex],
]
// predicate limit_amplitude(max_amp: float)(ls: list[float]) = max(ls) - min(ls) <= max_amp
// type LowAmplitudeWave = list[float where _ >= 1] where limit_amplitude(10)(_)
predicate limit_amplitude(max_amp: float)(ls: list[float]) = max(ls) - min(ls) <= max_amp
type LowAmplitudeWave = list[float where _ >= 1] where limit_amplitude(10)(_)
@@ -11,7 +11,7 @@ delta = end_ts - start_ts
min_temp, max_temp = -30.0, 100.0
min_pres, max_pres = 800.0, 1100.0
min_hum, max_hum = 0.0, 100.0
min_hum, max_hum = 0.0, 1.0
N = 3000
@@ -47,7 +47,7 @@ def daily_avg(df: DataWithHI):
DailyAverages,
df.groupby(
by=[
"station_id",
df["station_id"],
df["timestamp"].dt.day.rename("day"),
],
)
+9
View File
@@ -136,6 +136,15 @@ class ConstraintType:
constraint: Expr
class ComplexType:
members: list[MemberStmt]
class ExtensionType:
base: Type
extension: ComplexType
class FunctionType:
params: ParamSpec
returns: Type
+23
View File
@@ -256,6 +256,12 @@ class Type(ABC):
@abstractmethod
def visit_constraint_type(self, type: ConstraintType) -> T: ...
@abstractmethod
def visit_complex_type(self, type: ComplexType) -> T: ...
@abstractmethod
def visit_extension_type(self, type: ExtensionType) -> T: ...
@abstractmethod
def visit_function_type(self, type: FunctionType) -> T: ...
@@ -289,6 +295,23 @@ class ConstraintType(Type):
return visitor.visit_constraint_type(self)
@dataclass(frozen=True)
class ComplexType(Type):
members: list[MemberStmt]
def accept(self, visitor: Type.Visitor[T]) -> T:
return visitor.visit_complex_type(self)
@dataclass(frozen=True)
class ExtensionType(Type):
base: Type
extension: ComplexType
def accept(self, visitor: Type.Visitor[T]) -> T:
return visitor.visit_extension_type(self)
@dataclass(frozen=True)
class FunctionType(Type):
params: ParamSpec
+13
View File
@@ -124,6 +124,19 @@ class MidasPrinter(
res += " where " + type.constraint.accept(self)
return res
def visit_complex_type(self, type: m.ComplexType) -> str:
res: str = "{\n"
self.level += 1
for member in type.members:
res += member.accept(self)
res += "\n"
self.level -= 1
res += self.indented("}")
return res
def visit_extension_type(self, type: m.ExtensionType) -> str:
return f"{type.base.accept(self)} & {type.extension.accept(self)}"
def visit_function_type(self, type: m.FunctionType) -> str:
spec: str = self._visit_param_spec(type.params)
return f"fn {spec} -> {type.returns.accept(self)}"
+15
View File
@@ -177,6 +177,21 @@ class MidasAstPrinter(
with self._child_level(single=True):
type.constraint.accept(self)
def visit_complex_type(self, type: m.ComplexType) -> None:
self._write_line("ComplexType")
with self._child_level():
self._write_sequence("members", type.members, last=True)
def visit_extension_type(self, type: m.ExtensionType) -> None:
self._write_line("ExtensionType")
with self._child_level():
self._write_line("base")
with self._child_level(single=True):
type.base.accept(self)
self._write_line("extension", last=True)
with self._child_level(single=True):
type.extension.accept(self)
def visit_function_type(self, type: m.FunctionType) -> None:
self._write_line("FunctionType")
with self._child_level():
+17 -18
View File
@@ -51,25 +51,24 @@ class FrameGroupByMethodRegistry(MethodRegistry[Call]):
new_columns: list[DataFrameType.Column] = []
for column in call.groupby.frame.columns:
with self.reporter.with_context(f"in column '{column.name}'"):
column_groupby: ColumnGroupBy = ColumnGroupBy(column=column.type)
result_type: Type = self.typer.call_method(
location=call.location,
call_expr=call.call_expr,
obj=(call.groupby_expr, column_groupby),
method_name=method,
positional=call.positional,
keywords=call.keywords,
)
if not isinstance(result_type, ColumnType):
result_type = ColumnType(type=UnknownType())
new_columns.append(
DataFrameType.Column(
index=column.index,
name=column.name,
type=result_type,
)
column_groupby: ColumnGroupBy = ColumnGroupBy(column=column.type)
result_type: Type = self.typer.call_method(
location=call.location,
call_expr=call.call_expr,
obj=(call.groupby_expr, column_groupby),
method_name=method,
positional=call.positional,
keywords=call.keywords,
)
if not isinstance(result_type, ColumnType):
result_type = ColumnType(type=UnknownType())
new_columns.append(
DataFrameType.Column(
index=column.index,
name=column.name,
type=result_type,
)
)
return DataFrameType(columns=new_columns)
+2 -59
View File
@@ -159,10 +159,7 @@ class FrameMethodRegistry(MethodRegistry[Call]):
col_type2 = ColumnType(type=operand[1])
if col_type2 is not None:
with self.reporter.with_context(f"in column '{column.name}'"):
col_type = self._get_method_result(
call, col_type1, col_type2, method
)
col_type = self._get_method_result(call, col_type1, col_type2, method)
new_column = DataFrameType.Column(
index=column.index,
@@ -598,62 +595,8 @@ class FrameMethodRegistry(MethodRegistry[Call]):
)
return result.result
def _filter_groupby_columns(
self, frame: DataFrameType, by: TypedExpr
) -> DataFrameType:
"""Remove columns passed as string literals in groupby's `by` argument
Args:
frame (DataFrameType): the original dataframe
by (TypedExpr): the by argument
Returns:
DataFrameType: the filtered dataframe
"""
by_columns: list[str] = []
by_expr, _ = by
match by_expr:
case p.ListExpr(items=items):
for item in items:
match item:
case p.LiteralExpr(value=str() as name):
by_columns.append(name)
case p.LiteralExpr(value=str() as name):
by_columns.append(name)
if len(by_columns) == 0:
return frame
new_columns: list[DataFrameType.Column] = []
for column in frame.columns:
if column.name in by_columns:
continue
new_columns.append(
DataFrameType.Column(
index=len(new_columns),
name=column.name,
type=column.type,
)
)
return DataFrameType(columns=new_columns)
@method()
def groupby(self, call: Call) -> Type:
new_frame: DataFrameType = call.frame
by: Optional[TypedExpr] = None
if len(call.positional) != 0:
by = call.positional[0]
elif "by" in call.keywords:
by = call.keywords["by"]
if by is not None:
new_frame = self._filter_groupby_columns(call.frame, by)
bool_: Type = self.types.get_type("bool")
function: Function = Function(
params=ParamSpec(
@@ -683,7 +626,7 @@ class FrameMethodRegistry(MethodRegistry[Call]):
)
],
),
returns=FrameGroupBy(frame=new_frame),
returns=FrameGroupBy(frame=call.frame),
)
result: CallResult = self.dispatcher.get_result(
+16 -11
View File
@@ -13,9 +13,11 @@ from midas.checker.registry import TypesRegistry
from midas.checker.reporter import FileReporter, Reporter
from midas.checker.types import (
ColumnType,
ComplexType,
ConstraintType,
DataFrameType,
DerivedType,
ExtensionType,
Function,
GenericType,
ParamSpec,
@@ -405,21 +407,24 @@ class MidasTyper(m.Stmt.Visitor[None], m.Expr.Visitor[Type], m.Type.Visitor[Type
return UnknownType()
def visit_constraint_type(self, type: m.ConstraintType) -> Type:
base_type: Type = type.type.accept(self)
self._predicate_params["_"] = base_type
constraint_type: Type = self.type_of(type.constraint)
self._predicate_params = {}
if not self.types.is_subtype(constraint_type, self._bool):
self.reporter.error(
type.location,
f"Constraint must evaluate to a boolean, got {constraint_type}",
)
return ConstraintType(
type=base_type,
type=type.type.accept(self),
constraint=type.constraint,
)
def visit_complex_type(self, type: m.ComplexType) -> ComplexType:
return ComplexType(
members={
member.name.lexeme: member.type.accept(self) for member in type.members
}
)
def visit_extension_type(self, type: m.ExtensionType) -> Type:
return ExtensionType(
base=type.base.accept(self),
extension=self.visit_complex_type(type.extension),
)
def visit_function_type(self, type: m.FunctionType) -> Type:
return Function(
params=self._visit_param_spec(type.params),
-3
View File
@@ -257,9 +257,6 @@ class PythonTyper(
"""
unfolded: Type = unfold_type(obj[1])
match unfolded:
case TopType() | UnknownType():
return UnknownType()
case DataFrameType():
return self.frame_mgr.call(
method=method_name,
+24
View File
@@ -8,9 +8,11 @@ from midas.checker.types import (
AppliedType,
BaseType,
ColumnType,
ComplexType,
ConstraintType,
DataFrameType,
DerivedType,
ExtensionType,
Function,
GenericType,
OverloadedFunction,
@@ -200,6 +202,14 @@ class TypesRegistry:
case (BaseType(name=name1), BaseType(name=name2)):
return self.is_builtin_subtype(name1, name2)
case (ComplexType(properties=props1), ComplexType(properties=props2)):
for k, t in props2.items():
if k not in props1:
return False
if not self.is_subtype(props1[k], t):
return False
return True
case (DataFrameType(columns=columns1), DataFrameType(columns=columns2)):
# TODO: check order?
by_name1: dict[str, DataFrameType.Column] = {
@@ -496,6 +506,20 @@ class TypesRegistry:
member_type2 = substitute_typevars(member_type2, substitutions)
return member_type2
case ComplexType(members=members):
if member_name in members:
return members[member_name]
self.logger.debug(f"No member '{member_name}' in {type}")
return None
case ExtensionType(base=base, extension=ComplexType(members=members)):
if member_name in members:
return members[member_name]
self.logger.debug(
f"No member '{member_name}' on {type}, looking up in base"
)
return self.lookup_member(base, member_name)
case ConstraintType(type=base):
return self.lookup_member(base, member_name)
-12
View File
@@ -1,6 +1,5 @@
from __future__ import annotations
from contextlib import contextmanager
from typing import Optional
from midas.ast.location import Location
@@ -55,7 +54,6 @@ class FileReporter:
def __init__(self, base_reporter: Reporter, path: Optional[str]) -> None:
self.base_reporter: Reporter = base_reporter
self.path: Optional[str] = path
self._context: list[str] = []
def for_file(self, path: Optional[str]) -> FileReporter:
"""Create a new file reporter for the given path with the same base reporter
@@ -68,14 +66,6 @@ class FileReporter:
"""
return FileReporter(self.base_reporter, path)
@contextmanager
def with_context(self, ctx: str):
self._context.append(ctx)
try:
yield
finally:
self._context.pop()
def report(self, type: DiagnosticType, location: Location, message: str):
"""Report a diagnostic to the base reporter
@@ -84,8 +74,6 @@ class FileReporter:
location (Location): the location of the diagnostic in the file
message (str): the diagnostic's message
"""
for ctx in self._context:
message = message + ", " + ctx
self.base_reporter.report(self.path, type, location, message)
def error(self, location: Location, message: str):
+45
View File
@@ -113,6 +113,28 @@ class OverloadedFunction:
return "<overloaded function>"
@dataclass(frozen=True, kw_only=True)
class ComplexType:
"""A type with inline members"""
members: dict[str, Type]
def __str__(self) -> str:
props: list[str] = [f"{name}: {type}" for name, type in self.members.items()]
return f"{{{', '.join(props)}}}"
@dataclass(frozen=True, kw_only=True)
class ExtensionType:
"""An extension of a type, adding members through a `ComplexType`"""
base: Type
extension: ComplexType
def __str__(self) -> str:
return f"{self.base} & {self.extension}"
class Variance(StrEnum):
"""The variance of a :class:`TypeVar`"""
@@ -301,6 +323,24 @@ def substitute_typevars(type: Type, substitutions: dict[str, Type]) -> Type:
]
)
case ComplexType(members=members):
members2: dict[str, Type] = {
name: substitute_typevars(prop, substitutions)
for name, prop in members.items()
}
return ComplexType(members=members2)
case ExtensionType(base=base, extension=ComplexType(members=members)):
return ExtensionType(
base=substitute_typevars(base, substitutions),
extension=ComplexType(
members={
name: substitute_typevars(prop, substitutions)
for name, prop in members.items()
}
),
)
case AppliedType(name=name, args=args, body=body):
return AppliedType(
name=name,
@@ -428,6 +468,9 @@ def to_annotation(type: Type) -> str:
case OverloadedFunction():
return "Callable"
case ComplexType() | ExtensionType():
raise NotImplementedError
case TypeVar(name=name):
return name
@@ -476,6 +519,8 @@ Type = (
| UnitType
| Function
| OverloadedFunction
| ComplexType
| ExtensionType
| TypeVar
| GenericType
| AppliedType
+10
View File
@@ -338,11 +338,21 @@ class MidasHighlighter(
type.type.accept(self)
type.constraint.accept(self)
def visit_complex_type(self, type: m.ComplexType) -> None:
self.wrap(type, "complex-type")
for member in type.members:
member.accept(self)
def visit_function_type(self, type: m.FunctionType) -> None:
self.wrap(type, "function")
self._visit_param_spec(type.params)
type.returns.accept(self)
def visit_extension_type(self, type: m.ExtensionType) -> None:
self.wrap(type, "extension")
type.base.accept(self)
type.extension.accept(self)
def _visit_param_spec(self, spec: m.ParamSpec) -> None:
for param in spec.pos + spec.mixed + spec.kw:
param.type.accept(self)
+2 -1
View File
@@ -7,7 +7,8 @@ span {
&.named-type,
&.generic-type,
&.constraint-type {
&.constraint-type,
&.complex-type {
--col: 150, 150, 150;
}
+4
View File
@@ -16,9 +16,11 @@ from midas.checker.types import (
BaseType,
ColumnGroupBy,
ColumnType,
ComplexType,
ConstraintType,
DataFrameType,
DerivedType,
ExtensionType,
FrameGroupBy,
Function,
GenericType,
@@ -673,6 +675,8 @@ class Generator(p.Stmt.Visitor[ast.stmt], p.Expr.Visitor[ast.expr]):
case (
Function()
| OverloadedFunction()
| ComplexType()
| ExtensionType()
| GenericType()
| FrameGroupBy()
| ColumnGroupBy()
+10
View File
@@ -8,9 +8,11 @@ from midas.checker.types import (
BaseType,
ColumnGroupBy,
ColumnType,
ComplexType,
ConstraintType,
DataFrameType,
DerivedType,
ExtensionType,
FrameGroupBy,
Function,
GenericType,
@@ -267,6 +269,14 @@ class StubsGenerator:
right=self.dump_type(overloads[-1]),
)
case ComplexType():
name: str = self.new_stub_name()
self.generate_stub(name, type)
return ast.Name(id=name)
case ExtensionType():
raise NotImplementedError
case TypeVar():
return ast.Name(id=type.name)
+46 -3
View File
@@ -5,9 +5,11 @@ from midas.ast.midas import (
AliasStmt,
BinaryExpr,
CallExpr,
ComplexType,
ConstraintType,
Expr,
ExtendStmt,
ExtensionType,
FrameType,
FunctionType,
GenericType,
@@ -185,9 +187,19 @@ class MidasParser(Parser[list[Stmt]]):
Returns:
TypeExpr: the parsed type expression
"""
base: Type
if self.match(TokenType.FUNC):
return self.function()
return self.constraint_type()
base = self.function()
else:
base = self.constraint_type()
if self.match(TokenType.AND):
extension: ComplexType = self.complex_type()
return ExtensionType(
location=Location.span(base.location, extension.location),
base=base,
extension=extension,
)
return base
def constraint_type(self) -> Type:
"""Parse a constraint type expression
@@ -223,6 +235,9 @@ class MidasParser(Parser[list[Stmt]]):
self.consume(TokenType.RIGHT_PAREN, "Unclosed parenthesis")
return type
if self.check(TokenType.LEFT_BRACE):
return self.complex_type()
return self.generic_type()
def generic_type(self) -> Type:
@@ -280,6 +295,34 @@ class MidasParser(Parser[list[Stmt]]):
name=name,
)
def complex_type(self) -> ComplexType:
"""Parse a complex type expression
A complex type consists of zero or more member statements enclosed in
curly braces
Returns:
ComplexType: the parsed complex type expression
"""
left: Token = self.consume(
TokenType.LEFT_BRACE, "Expected '{' to start type body"
)
members: list[MemberStmt] = []
# TODO: add keyword to differentiate properties and methods,
# and allow multiple methods with the same name but not properties
names: set[str] = set()
while not self.check(TokenType.RIGHT_BRACE) and not self.is_at_end():
member: MemberStmt = self.member_stmt()
# if member.name.lexeme in names:
# raise self.error(member.name, "Duplicate property")
# names.add(member.name.lexeme)
members.append(member)
right: Token = self.consume(TokenType.RIGHT_BRACE, "Unclosed type body")
return ComplexType(
location=left.location_to(right),
members=members,
)
def frame_type(self) -> FrameType:
"""Parse a frame type expression
@@ -589,7 +632,7 @@ class MidasParser(Parser[list[Stmt]]):
return WildcardExpr(location=token.get_location(), token=token)
if self.match(TokenType.LEFT_PAREN):
expr: Expr = self.expression()
expr: Expr = self.constraint()
right: Token = self.consume(TokenType.RIGHT_PAREN, "Unclosed parenthesis")
return GroupingExpr(location=token.location_to(right), expr=expr)
+2 -2
View File
@@ -380,7 +380,7 @@ class PythonParser:
for col in cols:
columns.append(self._parse_frame_column(col))
case ast.Slice() | ast.Name() | ast.Subscript():
case ast.Slice() | ast.Name():
columns.append(self._parse_frame_column(schema))
case _:
@@ -391,7 +391,7 @@ class PythonParser:
def _parse_frame_column(self, column: ast.expr) -> FrameColumn:
loc: Location = Location.from_ast(column)
match column:
case ast.Name() | ast.Subscript():
case ast.Name():
return FrameColumn(
location=loc,
name=None,
+17 -5
View File
@@ -1,8 +1,20 @@
Identifier ::= '[a-zA-Z][a-zA-Z_]*'
identifier ::= '[a-zA-Z][a-zA-Z_]*'
TypeArgs ::= "[" (Type ("," Type)*)? "]"
integer ::= '\d+'
number ::= integer ["." integer]
boolean ::= "False" | "True"
none ::= "None"
FrameColumn ::= ((Identifier | "_") ":")? Type
FrameSchema ::= "[" (FrameColumn ("," FrameColumn)*)? "]"
value ::= number | boolean | none
lambda-value ::= "_" | value
lambda-operator ::= ">" | "<" | ">=" | "<=" | "==" | "!="
lambda ::= lambda-value lambda-operator lambda-value
Type ::= "Frame" FrameSchema | Identifier TypeArgs?
constraint ::= identifier | "(" lambda ")"
base-type ::= identifier
type ::= base-type { "+" constraint }
column-type ::= type | "_"
column-def ::= [ identifier ":" ] column-type
frame-def ::= column-def { "," column-def }
+52 -67
View File
@@ -1,72 +1,64 @@
#import "@preview/fervojo:0.1.1": default-css, render
#import "@preview/fervojo:0.1.1": render
#let extra-css = ```css
svg.railroad .terminal rect {
fill: #F7DCD4;
}
```
#let css = default-css() + bytes(extra-css.text)
#let type-args = ```
{[`type-args` "[" <!, 'type'*","> "]"]}
```
#let frame-schema = ```
{[`frame-schema` "[" <!, [[<'identifier', "_"> ":"]? 'type']*","> "]"]}
```
#let type = ```
{[`type` <
["Frame" 'frame-schema'],
['identifier' <!, 'type-args'>]
#let value = ```
{[`value` <
[`number` 'digit' * ! <!, ["." 'digit' * !]>],
[`boolean` <"False", "True">],
[`none` "None"]
>]}
```
#let rules = (
type-args: type-args,
frame-schema: frame-schema,
type: type,
)
#let constraint = ```
{[`constraint` <"_", 'value'> <">", "<", ">=", "<=", "==", "!="> <"_", 'value'>]}
```
#let inline = (
"type-args",
"frame-schema",
#let type-with-constraints = ```
{[`type-with-constraints` 'identifier' <!, ["+" "(" 'constraint' ")"] * !>]}
```
#let column-def = ```
{[`column-def` <!, ['identifier' ":"]> <"_", 'type-with-constraints'>]}
```
#let frame-def = ```
{[`frame-def` 'column-def' * ","]}
```
#let annotation = ```
{[`annotation` 'identifier' <!, ["[" 'frame-def' "]"]>]}
```
#let rules = (
value,
constraint,
type-with-constraints,
column-def,
frame-def,
annotation,
)
#set text(font: "Source Sans 3")
#title[Supported Python annotation syntax]
= Type annotation syntax
= Outline
#box(
columns(
2,
outline(title: none),
),
height: 9cm,
stroke: 1pt,
inset: 1em,
)
= Statements and expressions
#for (name, rule) in rules.pairs().rev() {
[== #name]
render(rule, css: css)
#for rule in rules {
render(rule)
}
/*
#let by-name = (
annotation: annotation,
frame-def: frame-def,
column-def: column-def,
type-with-constraints: type-with-constraints,
constraint: constraint,
value: value,
)
#let substitute(base-rule) = {
let new-rule = base-rule
for name in inline {
let rule = rules.at(name)
let replacement = rule.text.slice(1, -1).replace(regex("\[`.*?`"), "[")
replacement = "[" + replacement + "#`" + name + "`]"
new-rule = new-rule.replace(
"'" + name + "'",
replacement,
)
for (key, rule) in by-name.pairs() {
new-rule = new-rule.replace("'" + key + "'", rule.text.slice(1, -1))
}
if new-rule != base-rule {
new-rule = substitute(new-rule)
@@ -74,16 +66,9 @@ svg.railroad .terminal rect {
return new-rule
}
#let combined = raw(substitute(annotation.text))
#set page(flipped: true)
= Combined rules
#for (name, rule) in rules.pairs() {
if not name in inline {
[== #name]
let combined = substitute(rule.text)
render(raw(combined), css: css)
//raw(block: true, combined)
}
}
#render(combined)
*/
+15 -60
View File
@@ -4,85 +4,40 @@ Identifier ::= [a-zA-Z_] [a-zA-Z_0-9]*
Integer ::= '\d+'
Number ::= "-"? Integer ("." Integer)?
Boolean ::= "False" | "True"
String ::= '(".*?")|(\'.*?\')'
None ::= "None"
Literal ::= Number | Boolean | String | None
Value ::= Number | Boolean | None
UnaryOp ::= "+" | "-" | "!"
FactorOp ::= "*" | "/"
TermOp ::= "+" | "-"
ComparisonOp ::= ">" | "<" | ">=" | "<="
EqualityOp ::= "==" | "!="
PosArg ::= Expression
KwArg ::= Identifier "=" Expression
PosArgs ::= PosArg ("," PosArg)*
KwArgs ::= KwArg ("," KwArg)*
Args ::= (
PosArgs
| KwArgs
| PosArgs "," KwArgs
)
Grouping ::= "(" Expression ")"
Primary ::= "_" | Literal | Identifier | Grouping
Grouping ::= "(" Constraint ")"
Primary ::= "_" | Value | Identifier | Grouping
Reference ::= Primary ("." Identifier)*
CallArgs ::= "(" Args ")"
Call ::= Reference CallArgs*
Unary ::= UnaryOp Unary | Call
Factor ::= Unary (FactorOp Unary)*
Term ::= Factor (TermOp Factor)*
Comparison ::= Term (ComparisonOp Term)*
Unary ::= "-"? Unary | Reference
Comparison ::= Unary (ComparisonOp Unary)*
Equality ::= Comparison (EqualityOp Comparison)*
Expression ::= Equality ("&" Equality)*
Constraint ::= Expression
Constraint ::= Equality ("&" Equality)*
TemplateParam ::= Identifier ("<:" Type)?
Template ::= "[" (TemplateParam ("," TemplateParam)*)? "]"
ParamType ::= Type "?"?
PosParam ::= (Identifier ":")? ParamType
KwParam ::= Identifier ":" ParamType
PosParams ::= (
(PosParam ("," PosParam)* ("," "/")?)
| "/"
)
MixedParams ::= KwParam ("," KwParam)
KwParams ::= (
(("*", ",")? KwParam ("," KwParam)*)
| "*"
)
Params ::= (
PosParams
| MixedParams
| KwParams
| (PosParams "," MixedParams)
| (PosParams "," KwParams)
| (MixedParams "," KwParams)
| (PosParams "," MixedParams "," KwParams)
)
ParamSpec ::= "(" Params? ")"
TypeProperty ::= Identifier ":" Type
ComplexType ::= "{" TypeProperty* "}"
NamedType ::= Identifier
TypeArgs ::= "[" (Type ("," Type)*)? "]"
FrameColumn ::= TOKEN ":" Type
FrameSchema ::= "[" (FrameColumn ("," FrameColumn)*)? "]"
GenericType ::= "Frame" FrameSchema | NamedType TypeArgs?
TypeParams ::= "[" (Type ("," Type)*)? "]"
GenericType ::= NamedType TypeParams?
GroupedType ::= "(" Type ")"
BaseType ::= GroupedType | GenericType
BaseType ::= GroupedType | ComplexType | GenericType
ConstraintType ::= BaseType ("where" Constraint)?
FuncType ::= "fn" ParamSpec "->" Type
Type ::= ConstraintType
MemberStatement ::= ("prop" | "def") Identifier ":" Type
ExtendBody ::= "{" MemberStatement* "}"
OpDefinition ::= "op" Identifier "(" Type ")" "->" Type
ExtendBody ::= "{" OpDefinition* "}"
AliasStatement ::= "alias" Identifier "=" Type
TypeStatement ::= "type" Identifier Template? "=" Type
ExtendStatement ::= "extend" Type ExtendBody
PredicateStatement ::= "predicate" Identifier ParamSpec* "=" Constraint
PredicateStatement ::= "predicate" Identifier "(" Identifier ":" Type ")" "=" Constraint
Statement ::= AliasStatement | TypeStatement | ExtendStatement | PredicateStatement
Statement ::= TypeStatement | ExtendStatement | PredicateStatement
+34 -84
View File
@@ -7,61 +7,40 @@ svg.railroad .terminal rect {
```
#let css = default-css() + bytes(extra-css.text)
#let literal = ```
{[`literal` <
#let value = ```
{[`value` <
[`number` 'digit' * ! <!, ["." 'digit' * !]>],
[`boolean` <"False", "True">],
[`string` <["\"" 'char'*! "\""], ["'" 'char'*! "'"]>],
[`none` "None"]
>]}
```
#let grouping = ```
{[`grouping` "(" 'expression' ")"]}
{[`grouping` "(" 'constraint' ")"]}
```
#let primary = ```
{[`primary` <"_", 'literal', 'identifier', 'grouping'>]}
{[`primary` <"_", 'value', 'identifier', 'grouping'>]}
```
#let reference = ```
{[`reference` 'primary' <!, ["." 'identifier']*!>]}
```
#let call-args = ```
{[`call-args` "(" <!, <'expression', ['identifier' "=" 'expression']>*","#`Same rules as Python`> ")"]}
```
#let call = ```
{[`call` 'reference' <!, 'call-args'*!>]}
```
#let unary = ```
{[`unary` <[<"+", "-", "!"> 'unary'], 'call'>]}
```
#let factor = ```
{[`factor` 'unary'*<"*", "/">]}
```
#let term = ```
{[`term` 'factor'*<"+", "-">]}
{[`unary` <[<!, "-"> 'unary'], 'reference'>]}
```
#let comparison = ```
{[`comparison` 'term'*<">", "<", ">=", "<=">]}
{[`comparison` 'unary'*<">", "<", ">=", "<=">]}
```
#let equality = ```
{[`equality` 'comparison'*<"==", "!=">]}
```
#let expression = ```
{[`expression` 'equality'*"&"]}
```
#let constraint = ```
{[`constraint` 'expression']}
{[`constraint` 'equality'*"&"]}
```
#let template-param = ```
@@ -72,20 +51,24 @@ svg.railroad .terminal rect {
{[`template` "[" <!, 'template-param'*","> "]"]}
```
#let type-property = ```
{[`type-property` 'identifier' ":" 'type']}
```
#let complex-type = ```
{[`complex-type` "{" <!, 'type-property'*!> "}"]}
```
#let named-type = ```
{[`named-type` 'identifier']}
```
#let type-args = ```
{[`type-args` "[" <!, 'type'*","> "]"]}
```
#let frame-schema = ```
{[`frame-schema` "[" <!, ['TOKEN' ":" 'type']*","> "]"]}
#let type-params = ```
{[`type-params` "[" <!, 'type'*","> "]"]}
```
#let generic-type = ```
{[`generic-type` <["Frame" 'frame-schema'], ['named-type' <!, 'type-args'>]>]}
{[`generic-type` 'named-type' <!, 'type-params'>]}
```
#let grouped-type = ```
@@ -93,88 +76,59 @@ svg.railroad .terminal rect {
```
#let base-type = ```
{[`base-type` <'grouped-type', 'generic-type'>]}
{[`base-type` <'grouped-type', 'complex-type', 'generic-type'>]}
```
#let constraint-type = ```
{[`constraint-type` 'base-type' <!, ["where" 'constraint']>]}
```
#let pos-param = ```
{[`pos-param` <!, ['identifier' ":"]> 'type' <!, "?">]}
```
#let kw-param = ```
{[`kw-param` 'identifier' ":" 'type' <!, "?">]}
```
#let param-spec = ```
{[`param-spec` "(" <!, <'pos-param', "/", "*", 'kw-param'>*",">#`Same rules as Python` ")"]}
```
#let func-type = ```
{[`func-type` "fn" 'param-spec' "->" 'type']}
```
#let type = ```
{[`type` <'func-type', 'constraint-type'>]}
```
#let alias-statement = ```
{[`alias-statement` "alias" 'identifier' "=" 'type']}
{[`type` 'constraint-type']}
```
#let type-statement = ```
{[`type-statement` "type" 'identifier' <!, 'template'> "=" 'type']}
```
#let member-stmt = ```
{[`member-stmt` <"prop", "def"> 'identifier' ":" 'type']}
#let op-definition = ```
{[`op-definition` "op" 'identifier' "(" 'type' ")" "->" 'type']}
```
#let extend-statement = ```
{[`extend-statement` "extend" 'type' "{" <!, 'member-stmt'*!> "}"]}
{[`extend-statement` "extend" 'type' "{" <!, 'op-definition'*!> "}"]}
```
#let predicate-statement = ```
{[`predicate-statement` "predicate" 'identifier' <!, 'param-spec'*!> "=" 'constraint']}
{[`predicate-statement` "predicate" 'identifier' "(" 'identifier' ":" 'type' ")" "=" 'constraint']}
```
#let statement = ```
{[`statement` <'alias-statement', 'type-statement', 'extend-statement', 'predicate-statement'>]}
{[`statement` <'type-statement', 'extend-statement', 'predicate-statement'>]}
```
#let rules = (
literal: literal,
value: value,
grouping: grouping,
primary: primary,
reference: reference,
call-args: call-args,
call: call,
unary: unary,
factor: factor,
term: term,
comparison: comparison,
equality: equality,
expression: expression,
constraint: constraint,
template-param: template-param,
template: template,
type-property: type-property,
complex-type: complex-type,
named-type: named-type,
type-args: type-args,
type-params: type-params,
generic-type: generic-type,
grouped-type: grouped-type,
base-type: base-type,
constraint-type: constraint-type,
pos-param: pos-param,
kw-param: kw-param,
param-spec: param-spec,
func-type: func-type,
type: type,
alias-statement: alias-statement,
type-statement: type-statement,
member-stmt: member-stmt,
op-definition: op-definition,
extend-statement: extend-statement,
predicate-statement: predicate-statement,
statement: statement,
@@ -182,22 +136,18 @@ svg.railroad .terminal rect {
#let inline = (
"grouping",
"literal",
"value",
"template-param",
"template",
"type-property",
"call-args",
"type-args",
"complex-type",
"type-params",
"named-type",
"grouped-type",
"generic-type",
"base-type",
"constraint-type",
"pos-param",
"kw-param",
"func-type",
"member-stmt",
"alias-statement",
"op-definition",
"type-statement",
"extend-statement",
"predicate-statement",
@@ -214,7 +164,7 @@ svg.railroad .terminal rect {
2,
outline(title: none),
),
height: 15cm,
height: 9cm,
stroke: 1pt,
inset: 1em,
)
+3 -3
View File
@@ -1,8 +1,8 @@
# type: ignore
# ruff: disable [F821]
df1: Frame[i:int, a:int, b:float]
df2: Frame[i:int, a:int, b:float]
df1: Frame[a:int, b:float]
df2: Frame[a:int, b:float]
_: Any
@@ -38,7 +38,7 @@ _ = df1.sum()
_ = df1.var()
# Groupby
df_gb = df1.groupby(by="i")
df_gb = df1.groupby(by="a")
_ = df_gb.kurt()
_ = df_gb.max()
File diff suppressed because it is too large Load Diff
+12 -18
View File
@@ -9,22 +9,26 @@ type Longitude = float where (-180 <= _ <= 180)
type Difference[T] = T
// Complex custom type, containing two values accessible through properties
type GeoLocation = object
extend GeoLocation {
type GeoLocation = {
prop lat: Latitude
prop lon: Longitude
}
type GeoLocationDifference = object
// Define operations on our custom type
extend GeoLocation {
// This type is compatible with the `-` operation with another GeoLocation
// i.e. you can subtract a GeoLocation from another GeoLocation, resulting
// in a Difference of GeoLocations
def __sub__: fn(GeoLocation, /) -> Difference[GeoLocation]
}
extend GeoLocationDifference {
// For complex generics, you need to specify how the genericity the properties
// are handled
type Difference[GeoLocation] = {
prop lat: Difference[Latitude]
prop lon: Difference[Longitude]
}
// Define operations on our custom type
// Simple operation defined on our custom types
extend Latitude {
def __sub__: fn(Latitude, /) -> Difference[Latitude]
@@ -34,23 +38,13 @@ extend Longitude {
def __sub__: fn(Longitude, /) -> Difference[Longitude]
}
extend GeoLocation {
// This type is compatible with the `-` operation with another GeoLocation
// i.e. you can subtract a GeoLocation from another GeoLocation, resulting
// in a GeoLocationDifference
def __sub__: fn(GeoLocation, /) -> GeoLocationDifference
}
// Predefined custom predicates that can be referenced in other definitions
predicate Positive(v: float) = v >= 0
predicate StrictlyPositive(v: float) = v > 0
predicate Equatorial(loc: GeoLocation) = (-10 <= loc.lat <= 10)
predicate Arctic(loc: GeoLocation) = (loc.lat >= 66)
type Person = object
extend Person {
type Person = {
prop name: str
// Property with an inline constraint
File diff suppressed because it is too large Load Diff
+15
View File
@@ -4,9 +4,11 @@ from midas.ast.midas import (
AliasStmt,
BinaryExpr,
CallExpr,
ComplexType,
ConstraintType,
Expr,
ExtendStmt,
ExtensionType,
FrameType,
FunctionType,
GenericType,
@@ -170,6 +172,12 @@ class MidasAstJsonSerializer(
"constraint": type.constraint.accept(self),
}
def visit_complex_type(self, type: ComplexType) -> dict:
return {
"_type": "ComplexType",
"members": self._serialize_list(type.members),
}
def visit_function_type(self, type: FunctionType) -> dict:
return {
"_type": "FunctionType",
@@ -192,6 +200,13 @@ class MidasAstJsonSerializer(
"required": param.required,
}
def visit_extension_type(self, type: ExtensionType) -> dict:
return {
"_type": "ExtensionType",
"base": type.base.accept(self),
"extension": type.extension.accept(self),
}
def visit_frame_type(self, type: FrameType) -> dict:
return {
"_type": "FrameType",
+6 -3
View File
@@ -1,16 +1,19 @@
{
"brackets": [
["{", "}"],
["[", "]"]
["[", "]"],
["<", ">"]
],
"autoClosingPairs": [
{ "open": "{", "close": "}" },
{ "open": "[", "close": "]" },
{ "open": "(", "close": ")" }
{ "open": "(", "close": ")" },
{ "open": "<", "close": ">" }
],
"surroundingPairs": [
["{", "}"],
["[", "]"],
["(", ")"]
["(", ")"],
["<", ">"]
]
}
+5 -1
View File
@@ -10,6 +10,7 @@
{
"id": "midas",
"extensions": [
".mpy",
".midas"
],
"aliases": [
@@ -22,7 +23,10 @@
{
"language": "midas",
"scopeName": "source.midas",
"path": "./syntaxes/midas.tmLanguage.json"
"path": "./syntaxes/midas.tmLanguage.json",
"embeddedLanguages": {
"meta.embedded.block.python": "python"
}
}
]
}
+126 -517
View File
@@ -2,558 +2,167 @@
"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
"name": "Midas",
"scopeName": "source.midas",
"fileTypes": [
"midas"
],
"patterns": [
{
"include": "#comments"
},
{
"include": "#alias-stmt"
},
{
"include": "#type-stmt"
},
{
"include": "#extend-stmt"
},
{
"include": "#extend-body"
},
{
"include": "#predicate-stmt"
}
],
"patterns": [{ "include": "#statement" }],
"repository": {
"comments": {
"patterns": [
{
"begin": "//",
"beginCaptures": {
"0": {
"name": "punctuation.definition.comment.midas"
}
},
"end": "$",
"name": "comment.line.double-slash.midas"
},
{
"begin": "/\\*",
"beginCaptures": {
"0": {
"name": "punctuation.definition.comment.midas"
}
},
"end": "\\*/",
"endCaptures": {
"0": {
"name": "punctuation.definition.comment.midas"
}
},
"name": "comment.block.midas"
"comment": {
"begin": "(//)",
"end": "($)",
"name": "comment.line",
"beginCaptures": {
"1": {
"name": "comment.line.double-dash"
}
}
},
"type-def": {
"begin": "\\b(type)\\s+([a-zA-Z_][a-zA-Z_\\d]*)",
"end": "$",
"beginCaptures": {
"1": {
"name": "keyword.control.type.midas"
},
"2": {
"name" : "variable.name"
}
},
"patterns": [
{ "include": "#type-base" },
{ "include": "#type-body" }
]
},
"string": {
"begin": "\"",
"type-base": {
"begin": "(\\()([a-zA-Z_][a-zA-Z_\\d]*)(\\))",
"end": "$",
"beginCaptures": {
"1": {
"name": "punctuation.definition.base.begin.midas"
},
"2": {
"name": "variable.name"
},
"3": {
"name": "punctuation.definition.base.end.midas"
}
},
"patterns": [
{ "include": "#type-cond" }
]
},
"type-cond": {
"begin": "where",
"end": "$",
"beginCaptures": {
"0": {
"name": "punctuation.definition.string.begin.midas"
"name": "keyword.control.where.midas"
}
},
"end": "\"",
"endCaptures": {
"0": {
"name": "punctuation.definition.string.end.midas"
}
},
"name": "string.quoted.double.c"
}
},
"alias-stmt": {
"begin": "\\b(alias)\\b",
"beginCaptures": {
"1": {
"name": "keyword.declaration.midas"
}
},
"end": "$",
"name": "meta.declaration.alias.midas",
"patterns": [
{
"include": "#comments"
},
{
"match": "[A-Za-z_][A-Za-z0-9_]*",
"name": "entity.name.type.midas"
},
{
"begin": "=",
"beginCaptures": {
"0": {
"name": "keyword.operator.equal.midas"
}
},
"end": "$",
"patterns": [
{
"include": "#type-expr"
}
]
}
]
},
"type-stmt": {
"begin": "\\b(type)\\b",
"beginCaptures": {
"1": {
"name": "keyword.declaration.midas"
}
},
"end": "$",
"name": "meta.declaration.type.midas",
"patterns": [
{
"include": "#comments"
},
{
"match": "[A-Za-z_][A-Za-z0-9_]*",
"name": "entity.name.type.midas"
},
{
"begin": "\\[",
"beginCaptures": {
"0": {
"name": "punctuation.section.brackets.begin.midas"
}
},
"end": "\\]",
"endCaptures": {
"0": {
"name": "punctuation.section.brackets.end.midas"
}
},
"patterns": [
{
"include": "#comments"
},
{
"include": "#type-params"
}
]
},
{
"begin": "=",
"beginCaptures": {
"0": {
"name": "keyword.operator.equal.midas"
}
},
"end": "$",
"patterns": [
{
"include": "#type-expr"
}
]
}
]
},
"type-params": {
"patterns": [
{
"include": "#comments"
},
{
"match": "<:",
"name": "keyword.operator.subtype.midas"
},
{
"match": "[A-Za-z][A-Za-z0-9_]*",
"name": "entity.name.type.midas"
}
]
},
"extend-stmt": {
"begin": "\\b(extend)\\b",
"beginCaptures": {
"1": {
"name": "keyword.declaration.midas"
}
},
"end": "(?=\\{)|$",
"name": "meta.declaration.extend.midas",
"patterns": [
{
"include": "#comments"
},
{
"match": "[A-Za-z_][A-Za-z0-9_]*",
"name": "entity.name.type.midas"
},
{
"begin": "\\[",
"beginCaptures": {
"0": {
"name": "punctuation.section.brackets.begin.midas"
}
},
"end": "\\]",
"endCaptures": {
"0": {
"name": "punctuation.section.brackets.end.midas"
}
},
"patterns": [
{
"include": "#comments"
},
{
"include": "#type-params"
}
]
}
]
},
"extend-body": {
"type-body": {
"begin": "\\{",
"end": "\\}",
"beginCaptures": {
"0": {
"name": "punctuation.section.block.begin.midas"
"name": "punctuation.definition.type-body.begin.midas"
}
},
"end": "\\}",
"endCaptures": {
"0": {
"name": "punctuation.section.block.end.midas"
"name": "punctuation.definition.type-body.end.midas"
}
},
"patterns": [
{
"include": "#comments"
},
{
"include": "#member-stmt"
}
{"include": "#type-prop"},
{"include": "#comment"}
]
},
"member-stmt": {
"patterns": [
{
"include": "#comments"
"type-prop": {
"match": "([a-zA-Z_][a-zA-Z_\\d]*)(:)\\s*([a-zA-Z_][a-zA-Z_\\d]*)",
"captures": {
"1": {
"name": "variable.name"
},
{
"begin": "\\b(prop|def)\\b",
"beginCaptures": {
"1": {
"name": "keyword.other.midas"
}
},
"end": "$",
"patterns": [
{
"include": "#comments"
},
{
"match": "[A-Za-z_][A-Za-z0-9_]*",
"name": "variable.other.member.midas"
},
{
"begin": ":",
"beginCaptures": {
"0": {
"name": "punctuation.separator.annotation.midas"
}
},
"end": "$",
"patterns": [
{
"include": "#type-expr"
}
]
}
]
"2": {
"name": "punctuation.separator.annotation.midas"
},
"3": {
"name": "meta.type.name"
}
]
}
},
"predicate-stmt": {
"begin": "\\b(predicate)\\b",
"extend-def": {
"begin": "\\b(extend)\\s*([a-zA-Z_][a-zA-Z_\\d]*)\\s+(\\{)",
"end": "\\}",
"beginCaptures": {
"1": {
"name": "keyword.declaration.midas"
"name": "keyword.control.extend.midas"
},
"2": {
"name": "variable.name"
},
"3": {
"name": "punctuation.definition.extend-body.begin.midas"
}
},
"endCaptures": {
"0": {
"name": "punctuation.definition.extend-body.end.midas"
}
},
"patterns": [
{"include": "#op-def"},
{"include": "#comment"}
]
},
"op-def": {
"match": "\\b(op)\\s+(\\S+)\\s*\\(\\s*([a-zA-Z_][a-zA-Z_\\d]*)\\s*\\)\\s*(->)\\s*([a-zA-Z_][a-zA-Z_\\d]*)",
"captures": {
"1": {
"name": "keyword.control.op.midas"
},
"2": {
"name" : "keyword.operator"
},
"3": {
"name" : "variable.name"
},
"4": {
"name" : "keyword.operator.assignment"
},
"5": {
"name" : "variable.name"
}
}
},
"pred-def": {
"begin": "(predicate)\\s+([a-zA-Z_][a-zA-Z_\\d]*)\\(([a-zA-Z_][a-zA-Z_\\d]*):\\s*([a-zA-Z_][a-zA-Z_\\d]*)\\)\\s*(=)",
"end": "$",
"name": "meta.declaration.predicate.midas",
"patterns": [
{
"include": "#comments"
"beginCaptures": {
"1": {
"name": "keyword.control.pred.midas"
},
{
"match": "[A-Za-z_][A-Za-z0-9_]*",
"name": "entity.name.function.midas"
"2": {
"name": "variable.name"
},
{
"begin": "\\(",
"beginCaptures": {
"0": {
"name": "punctuation.section.group.begin.midas"
}
},
"end": "\\)",
"endCaptures": {
"0": {
"name": "punctuation.section.group.end.midas"
}
},
"patterns": [
{
"include": "#predicate-params"
}
]
"3": {
"name": "variable.name"
},
{
"begin": "=",
"beginCaptures": {
"0": {
"name": "keyword.operator.equal.midas"
}
},
"end": "$",
"patterns": [
{
"include": "#constraint"
}
]
"4": {
"name": "variable.name"
},
"5": {
"name": "keyword.operator.assignment"
}
},
"patterns": [
{ "include": "source.python" }
]
},
"predicate-params": {
"statement": {
"patterns": [
{
"include": "#comments"
},
{
"begin": "([A-Za-z_][A-Za-z0-9_]*)(:)",
"beginCaptures": {
"1": {
"name": "variable.parameter.midas"
},
"2": {
"name": "punctuation.separator.annotation.midas"
}
},
"end": "(?=,)|(?=\\))",
"patterns": [
{
"include": "#type-expr"
}
]
},
{
"match": ",",
"name": "punctuation.separator.midas"
}
]
},
"type-expr": {
"patterns": [
{
"include": "#comments"
},
{
"begin": "\\b(fn)\\s*(\\()",
"beginCaptures": {
"1": {
"name": "keyword.other.midas"
},
"2": {
"name": "punctuation.section.group.begin.midas"
}
},
"end": "\\)",
"endCaptures": {
"0": {
"name": "punctuation.section.group.end.midas"
}
},
"patterns": [
{
"include": "#fn-params"
}
]
},
{
"match": "->",
"name": "keyword.operator.arrow.midas"
},
{
"begin": "\\b(where)\\b",
"beginCaptures": {
"1": {
"name": "keyword.other.midas"
}
},
"end": "$",
"patterns": [
{
"include": "#constraint"
}
]
},
{
"begin": "(\\bFrame\\b)(\\s*)(\\[)",
"beginCaptures": {
"1": {
"name": "entity.name.type.midas"
},
"3": {
"name": "punctuation.section.brackets.begin.midas"
}
},
"end": "\\]",
"endCaptures": {
"0": {
"name": "punctuation.section.brackets.end.midas"
}
},
"patterns": [
{
"include": "#frame-schema"
}
]
},
{
"match": "\\bFrame\\b",
"name": "entity.name.type.midas"
},
{
"match": "[A-Za-z_][A-Za-z0-9_]*",
"name": "entity.name.type.midas"
},
{
"begin": "\\[",
"beginCaptures": {
"0": {
"name": "punctuation.section.brackets.begin.midas"
}
},
"end": "\\]",
"endCaptures": {
"0": {
"name": "punctuation.section.brackets.end.midas"
}
},
"patterns": [
{
"include": "#comments"
},
{
"include": "#type-expr"
},
{
"match": ",",
"name": "punctuation.separator.midas"
}
]
}
]
},
"fn-params": {
"patterns": [
{
"include": "#comments"
},
{
"begin": "([A-Za-z_][A-Za-z0-9_]*)(:)",
"beginCaptures": {
"1": {
"name": "variable.parameter.midas"
},
"2": {
"name": "punctuation.separator.annotation.midas"
}
},
"end": "(?=,)|(?=\\))",
"patterns": [
{
"include": "#type-expr"
},
{
"match": "\\?",
"name": "keyword.operator.qmark.midas"
}
]
},
{
"match": ",",
"name": "punctuation.separator.midas"
},
{
"include": "#type-expr"
}
]
},
"constraint": {
"patterns": [
{
"include": "#comments"
},
{
"match": "\\d+(\\.\\d+)?",
"name": "constant.numeric.midas"
},
{
"match": "\\b(true|false|none)\\b",
"name": "constant.language.midas"
},
{
"include": "#string"
},
{
"match": "(<=|>=|<|>|==|!=|&)",
"name": "keyword.operator.midas"
},
{
"match": "_",
"name": "variable.language.midas"
},
{
"match": "[A-Za-z_][A-Za-z0-9_]*(?=\\s*\\()",
"name": "variable.function.midas"
},
{
"match": "[A-Za-z_][A-Za-z0-9_]*",
"name": "variable.other.readwrite.midas"
}
]
},
"frame-schema": {
"patterns": [
{
"include": "#comments"
},
{
"match": "[A-Za-z_][A-Za-z0-9_]*",
"name": "variable.other.member.midas"
},
{
"begin": ":",
"beginCaptures": {
"0": {
"name": "punctuation.separator.annotation.midas"
}
},
"end": "(?=,)|(?=\\])",
"patterns": [
{
"include": "#type-expr"
}
]
},
{
"match": ",",
"name": "punctuation.separator.midas"
}
{ "include": "#comment" },
{ "include": "#type-def" },
{ "include": "#extend-def" },
{ "include": "#pred-def" }
]
}
}