%YAML 1.2
---
name: Sphinx
file_extensions:
  - sph
scope: source.sphinx
variables:
  identifier: \b[a-zA-Z_][a-zA-Z0-9_]*\b 
contexts:
  prototype:
    - include: comments
  main:
    - include: statements
  statements:
    - match: \b(end)\b
      scope: invalid
    - include: early-expressions
    - include: misc-keywords
    - include: while-statements
    - include: for-statements
    - include: control-flow-keywords
    - include: late-expressions
    - match: ';'
      scope: punctuation.terminator
  expressions:
    - include: early-expressions
    - include: late-expressions
  early-expressions:
    - include: constants
    - include: reserved-names
    - include: numbers
    - include: operators
    - include: declaration-keywords
    - include: strings
    - include: tuples
    - include: blocks
    - include: parens
    - include: subscripts
    - include: if-expressions
    - include: object-constructors
        - match: \,
      scope: punctuation.separator
  late-expressions:
    - include: function-defs
    - include: function-calls
    - include: class-defs
    - include: identifiers
    declaration-keywords:
    - match: \b(let|var)\b
      scope: keyword.declaration.other
    - match: \b(nonlocal)\b
      scope: keyword.other
  misc-keywords:
    - match: \b(del|echo|assert)\b
      scope: keyword.other
  control-flow-keywords:
    - match: \b(return)\b
      scope: keyword.control
    - match: \b(break|continue)\b\s*(::\w+)?
      captures:
        1: keyword.control
        2: entity.name.label
  constants:
    - match: \b(nil|true|false)\b
      scope: constant.language
  reserved-names:
    - match: \b(self|super)\b
      scope: variable.language
  numbers:
        - match: \b(\d+(?:\.\d*)?|\.\d+)\b
      scope: constant.numeric
        - match: \b(\d+)\b
      scope: constant.numeric
        - match: \b(0[xX])(\h*)\b
      scope: constant.numeric
  operators:
    - match: '[\+\-/\*%]'
      scope: keyword.operator.arithmetic
    - match: '[\~\&\|\^]|<<|>>'
      scope: keyword.operator.bitwise
    - match: <\=|>\=|\=\=|<|>|!\=
      scope: keyword.operator.comparison
    - match: \b(not|and|or)\b
      scope: keyword.operator.logical
    - match: \+\=|-\=|\*\=|/\=|%\=
      scope: keyword.operator.arithmetic keyword.operator.assignment
    - match: \~\=|&\=|\|\=|\^\=|>>\=|<<\=
      scope: keyword.operator.bitwise keyword.operator.assignment
    - match: \=
      scope: keyword.operator.assignment
  strings:
            - match: \"
      scope: punctuation.definition.string.begin
      push: inside_double_string
    - match: \'
      scope: punctuation.definition.string.begin
      push: inside_single_string
    - match: r\"
      scope: punctuation.definition.string.begin
      push: inside_double_string_raw
    - match: r\'
      scope: punctuation.definition.string.begin
      push: inside_single_string_raw
  inside_double_string:
    - meta_include_prototype: false
    - meta_scope: string.quoted.double
    - match: \"
      scope: punctuation.definition.string.end
      pop: true
    - include: string_escaped_char
  inside_single_string:
    - meta_include_prototype: false
    - meta_scope: string.quoted.single
    - match: \'
      scope: punctuation.definition.string.end
      pop: true
    - include: string_escaped_char
  inside_double_string_raw:
    - meta_include_prototype: false
    - meta_scope: string.quoted.double
    - match: \"
      scope: punctuation.definition.string.end
      pop: true
  inside_single_string_raw:
    - meta_include_prototype: false
    - meta_scope: string.quoted.single
    - match: \'
      scope: punctuation.definition.string.end
      pop: true
  string_escaped_char:
    - match: \\(?:\\|[nrt\'\"])
      scope: constant.character.escape
    - match: \\.
      scope: invalid
  identifiers:
    - match: '{{identifier}}'
      scope: variable.other
    - match: \.
      scope: punctuation.accessor
    - include: type_annotation
  type_annotation:
    - match: (:)\s*({{identifier}})
      captures:
        1: punctuation.separator
        2: storage.type
  tuples:
    - match: \,
      scope: punctuation.separator
  parens:
    - match: \(
      scope: punctuation.section.parens.begin
      push:
        - meta_scope: meta.parens
        - match: \)
          scope: punctuation.section.parens.end
          pop: true
        - include: expressions
        - include: type_annotation
  subscripts:
    - match: \[
      scope: punctuation.section.brackets.begin
      push:
        - meta_scope: meta.brackets
        - match: \]
          scope: punctuation.section.brackets.end
          pop: true
        - include: expressions
  function-calls:
    - match: (?={{identifier}}\s*\()
      push:
        - meta_scope: variable.function
        - match: '\('
          scope: punctuation.section.group.begin
          set: function-arg-list
  function-arg-list:
    - meta_scope: meta.group
    - match : \)
      scope: punctuation.section.group.end
      pop: true
    - match: \,
      scope: punctuation.separator
          - match: \.\.\.
      scope: keyword.operator
    - include: expressions
  blocks:
    - match: (::\w+)?\s*\b(begin)\b
      captures:
        1: entity.name.label
        2: keyword.other
            push:
        - meta_scope: meta.block
        - match: \b(end)\b
                    scope: keyword.other
          pop: true
        - include: statements
  if-expressions:
    - match: \b(if)\b
      scope: keyword.control.conditional
      push:
        - match: \b(then)\b
          scope: keyword.control.conditional
          set: if-body
        - include: expressions
  if-body:
    - meta_scope: meta.block
    - match: \b(elif)\b
      scope: keyword.control.conditional
      set:
        - match: \b(then)\b
          scope: keyword.control.conditional
          set: if-body
        - include: expressions
    - match: \b(else)\b
      scope: keyword.control.conditional
    - match: \b(end)\b
      scope: keyword.control.conditional
      pop: true
    - include: statements
  while-statements:
        - match: (::\w+)?\s*\b(while)\b
      captures:
        1: entity.name.label
        2: keyword.control
      push:
        - match: \b(do)\b
          scope: keyword.control
          set: while-body
        - include: expressions
        - match: \b(do)\b
      scope: keyword.control
      push:
        - meta_scope: meta.block
        - match: \b(while)\b
          scope: keyword.control
          pop: true
        - include: statements
  while-body:
    - meta_scope: meta.block
    - match: \b(end)\b
      scope: keyword.control
      pop: true
    - include: statements
  for-statements:
        - match: (::\w+)?\s*\b(for)\b
      captures:
        1: entity.name.label
        2: keyword.control
      push:
        - match: \b(in)\b
          scope: keyword.control
          set:
            - match: \b(do)\b
              scope: keyword.control
              set: for-body
            - include: expressions
        - include: expressions
  for-body:
    - meta_scope: meta.block
    - match: \b(end)\b
      scope: keyword.control
      pop: true
    - include: statements
  function-defs:
    - match: \b(fun)\b
      scope: keyword.declaration.function
      push:
        - meta_content_scope: entity.name.function
        - match: \(
          scope: punctuation.section.group.begin
          set: function-param-list
  function-param-list:
    - meta_scope: meta.group
    - match: \)(?=\s*->)
      scope: punctuation.section.group.end
      set: return-type-annotation
    - match: \)
      scope: punctuation.section.group.end
      set: function-body
    - match: \,
      scope: punctuation.separator
    - match: \b(self)\b        scope: variable.language
    - include: declaration-keywords
    - match: ({{identifier}})(\.\.\.)?
      captures:
        1: variable.parameter
        2: keyword.operator
    - include: type_annotation
    - match: '='
      scope: keyword.operator.assignment
      push:
        - match : '(?=[,)])'
          pop: true
        - include: expressions
  return-type-annotation:
    - match: (->)\s*({{identifier}})\s*(:)
      captures:
        1: keyword.operator
        2: storage.type
        3: punctuation.separator
      set: function-body
  function-body:
    - meta_scope: meta.block
    - match: \b(end)\b
      scope: keyword.other
      pop: true
    - include: statements
  class-defs:
    - match: \b(class)\b
      scope: keyword.declaration.class
      push:
        - meta_content_scope: entity.name.class
        - match: '\:'
          scope: punctuation.separator
          set: class-inheritance-list
        - match: (?=\b(var|fun|end)\b)
          set: class-body
  class-inheritance-list:
    - meta_scope: entity.other.inherited-class
    - match: \,
      scope: punctuation.separator
    - match: (?=\b(var|fun|end)\b)
      set: class-body
  class-body:
    - match: \b(end)\b
      scope: keyword.other
      pop: true
    - include: class-variable-defs
    - include: method-defs
    - include: expressions
  metamethod-names:
        - match: \b(__add|__mul|__sub|__div|__neg|__mod)\b
      scope: support.function
        - match: \b(__and|__or|__xor|__inv|__lshift|__rshift)\b
      scope: support.function
        - match: \b(__le|__lt|__eq|__bool)\b
      scope: support.function
        - match: \b(__getindex|__setindex|__delindex)\b
      scope: support.function
        - match: \b(__get|__set|__del)\b
      scope: support.function
        - match: \b(__call|__tostring)\b
      scope: support.function
        - match: \b(__\w+)\b
      scope: invalid
  class-variable-defs:
    - match: \b(var|let)\b
      scope: keyword.declaration.other
      push:
        - meta_content_scope: variable.parameter
        - include: metamethod-names
        - match: (?=\b(fun|var|let|init)\b)
          pop: true
        - match: \=
          scope: keyword.operator.assignment
          pop: true
        - match: \b(self)\b            scope: variable.language
        - match: \.
          scope: punctuation.accessor
        - include: type_annotation
  method-defs:
    - match: \b(fun)\b
      scope: keyword.declaration.function
      push:
        - meta_content_scope: entity.name.function
        - include: metamethod-names
        - match: \(
          scope: punctuation.section.group.begin
          set: function-param-list
  object-constructors:
    - match: \{
      scope: punctuation.section.braces.begin
      push: object-initializer-lvalue
  object-initializer-lvalue:
    - meta_scope: meta.braces
    - match: \}
      scope: punctuation.section.braces.end
      pop: true
    - match: '='
      scope: keyword.operator.assignment
      set: object-initializer-rvalue
    - match: '{{identifier}}'
      scope: variable.member
    - include: subscripts
    - include: type_annotation
  object-initializer-rvalue:
    - meta_scope: meta.braces
    - match: \}
      scope: punctuation.section.braces.end
      pop: true
    - match: \,
      scope: punctuation.separator
      set: object-initializer-lvalue
    - include: expressions
  comments:
        - match: \#\{
      scope: punctuation.definition.comment
      push:
        - meta_scope: comment.block
        - match: \}\#
          pop: true
        - include: comments
        - match: '#'
      scope: punctuation.definition.comment
      push:
        - meta_scope: comment.line
        - match: $\n?
          pop: true