@precedence {
  annotation
  member
  call
  exp @left
  mult @left
  add @left
  comp @left
  logic @left
  pipe @left
  range
}

@top Program {
  Shebang?
  statement*
}

statement[@isGroup=Statement] {
  ImportStatement { kw<"import"> ImportItems ImportFrom String } |
  FunctionDeclaration { kw<"export">? kw<"fn"> VariableDefinition ParamList Body } |
  VariableDeclaration { kw<"export">? VariableDefinition Equals expression } |
  TypeDeclaration { kw<"export">? kw<"type"> identifier ("=" type)? } |
  ReturnStatement { kw<"return"> expression } |
  ExpressionStatement { expression } |
  Annotation { AnnotationName AnnotationList? }
}

AnnotationList { !annotation "(" commaSep<AnnotationProperty> ")" }

ParamList { "(" commaSep<Parameter { VariableDefinition "?"? (":" type)? }> ")" }

Body { "{" statement* "}" }

ImportItems { commaSep1NoTrailingComma<ImportItem> }
ImportItem { identifier (ImportItemAs identifier)? }

expression[@isGroup=Expression] {
  String |
  Number |
  VariableName |
  TagDeclarator |
  kw<"true"> | kw<"false"> | kw<"nil"> |
  PipeSubstitution |
  BinaryExpression {
    expression !add AddOp expression |
    expression !mult MultOp expression |
    expression !exp ExpOp expression |
    expression !comp CompOp expression |
    expression !logic LogicOp expression
  } |
  UnaryExpression { UnaryOp expression } |
  ParenthesizedExpression { "(" expression ")" } |
  IfExpression { kw<"if"> expression Body kw<"else"> Body } |
  CallExpression { expression !call ArgumentList } |
  ArrayExpression { "[" commaSep<expression | IntegerRange { expression !range ".." expression }> "]" } |
  ObjectExpression { "{" commaSep<ObjectProperty> "}" } |
  MemberExpression { expression !member "." PropertyName } |
  SubscriptExpression { expression !member "[" expression "]" } |
  PipeExpression { expression (!pipe PipeOperator expression)+ }
}

UnaryOp { AddOp | BangOp }

ObjectProperty { PropertyName (":" | Equals) expression }

AnnotationProperty {
  PropertyName
  ( AddOp | MultOp | ExpOp | LogicOp | BangOp | CompOp | Equals | PipeOperator | PipeSubstitution )
  expression
}

LabeledArgument { ArgumentLabel Equals expression }

ArgumentList { "(" commaSep<LabeledArgument | expression> ")" }

type[@isGroup=Type] {
  PrimitiveType { identifier } |
  ArrayType { "[" type !member (";" Number "+"?)? "]" } |
  ObjectType { "{" commaSep<ObjectProperty { PropertyName ":" type }> "}" }
}

VariableDefinition { identifier }

VariableName { identifier ("::" identifier)*}

ArgumentLabel { identifier }

@skip { whitespace | LineComment | BlockComment }

kw<term> { @specialize[@name={term}]<identifier, term> }

commaSep<term> { (term ("," term)*)? ","? }

commaSep1NoTrailingComma<term> { term ("," term)* }

@tokens {
  String[isolate] { "'" ("\\" _ | !['\\])* "'" | '"' ("\\" _ | !["\\])* '"' }

  Number { "." @digit+ | @digit+ ("." @digit+)? }
  @precedence { Number, "." }

  AddOp { "+" | "-" }
  MultOp { "/" | "*" | "\\" }
  ExpOp { "^" }
  LogicOp { "|" | "&" }
  BangOp { "!" }
  CompOp { "==" | "!=" | "<=" | ">=" | "<" | ">" }
  Equals { "=" }
  PipeOperator { "|>" }

  PipeSubstitution { "%" }

  identifier { (@asciiLetter | "_") (@asciiLetter | @digit | "_")* }
  AnnotationName { "@" identifier? }
  PropertyName { identifier }
  TagDeclarator { "$" identifier }

  whitespace { @whitespace+ }

  LineComment[isolate] { "//" ![\n]* }
  BlockComment[isolate] { "/*" blockCommentRest }
  blockCommentRest { @eof | ![*] blockCommentRest | "*" blockCommentStar }
  blockCommentStar { @eof | "/" | ![/] blockCommentRest | "*" blockCommentStar }

  @precedence { LineComment, BlockComment, MultOp }

  Shebang { "#!" ![\n]* }

  ImportItemAs { "as" }
  ImportFrom { "from" }

  "(" ")"
  "{" "}"
  "[" "]"
  "," "?" ":" "." ".." ";" "::"
}

@external propSource kclHighlight from "./highlight"

@detectDelim
