<commit_before>const PREC = {
  compare: 2,
  conditional: 2,
  or: 10,
  and: 11,
  bitwise_or: 12,
  bitwise_and: 13,
  xor: 14,
  shift: 15,
  plus: 16,
  times: 17,
  power: 18,
  not: 1,
  negative: 19,
  call: 20,
  attribute: 20
}

module.exports = grammar({
  name: 'python',

  extras: $ => [
    $.comment,
    /\s|\\\n/
  ],

  externals: $ => [
    $._newline,
    $._indent,
    $._dedent
  ],

  rules: {
    module: $ => repeat($._statement),

    _statement: $ => choice(
      seq($._simple_statement, repeat1($._newline)),
      $._compound_statement
    ),

    // Simple statements

    _simple_statement: $ => choice(
      $.import_statement,
      $.import_from_statement,
      $.print_statement,
      $.assert_statement,
      $.expression_statement,
      $.return_statement,
      $.delete_statement,
      $.raise_statement,
      $.pass_statement,
      $.break_statement,
      $.continue_statement,
      $.global_statement
    ),

    import_statement: $ => seq(
      'import',
      $._import_list
    ),

    import_from_statement: $ => seq(
      'from',
      choice(
        seq(
          repeat('.'),
          $.dotted_name
        ),
        repeat1('.')
      ),
      'import',
      choice(
        $.wildcard_import,
        $._import_list,
        seq('(', $._import_list, ')')
      )
    ),

    _import_list: $ => seq(
      commaSep1(choice(
        $.dotted_name,
        $.aliased_import
      )),
      optional(',')
    ),

    aliased_import: $ => seq(
      $.dotted_name,
      'as',
      $.identifier
    ),

    wildcard_import: $ => '*',

    print_statement: $ => seq(
      'print',
      $._expression,
      repeat(seq(',', $._expression))
    ),

    assert_statement: $ => seq(
      'assert',
      $._expression,
      repeat(seq(',', $._expression))
    ),

    expression_statement: $ => choice(
      $._expression,
      $.assignment,
      $.augmented_assignment,
      $.yield,
      $.conditional_expression
    ),

    return_statement: $ => seq(
      'return',
      optional($.expression_list)
    ),

    delete_statement: $ => seq(
      'del',
      $.expression_list
    ),

    raise_statement: $ => seq(
      'raise',
      optional($.expression_list)
    ),

    pass_statement: $ => 'pass',
    break_statement: $ => 'break',
    continue_statement: $ => 'continue',

    // Compount statements

    _compound_statement: $ => choice(
      $.if_statement,
      $.for_statement,
      $.while_statement,
      $.try_statement,
      $.with_statement,
      $.function_definition,
      $.class_definition,
      $.decorated_definition
    ),

    if_statement: $ => seq(
      'if',
      $._expression,
      ':',
      $._suite,
      repeat($.elif_clause),
      optional($.else_clause)
    ),

    elif_clause: $ => seq(
      'elif',
      $._expression,
      ':',
      $._suite
    ),

    else_clause: $ => seq(
      'else',
      ':',
      $._suite
    ),

    for_statement: $ => seq(
      'for',
      $.variables,
      'in',
      $.expression_list,
      ':',
      $._suite,
      optional($.else_clause)
    ),

    while_statement: $ => seq(
      'while',
      $._expression,
      ':',
      $._suite,
      optional($.else_clause)
    ),

    try_statement: $ => seq(
      'try',
      ':',
      $._suite,
      choice(
        seq(
          repeat1($.except_clause),
          optional($.else_clause),
          optional($.finally_clause)
        ),
        $.finally_clause
      )
    ),

    except_clause: $ => seq(
      'except',
      optional(seq(
        $._expression,
        optional(seq(
          choice('as', ','),
          $._expression
        ))
      )),
      ':',
      $._suite
    ),

    finally_clause: $ => seq(
      'finally',
      ':',
      $._suite
    ),

    with_statement: $ => seq(
      'with',
      commaSep1($.with_item),
      ':',
      $._suite
    ),

    with_item: $ => seq(
      $._expression,
      optional(seq(
        'as',
        $._expression
      ))
    ),

    function_definition: $ => seq(
      'def',
      $.identifier,
      $.parameters,
      ':',
      $._suite
    ),

    parameters: $ => seq(
      '(',
      optional($._parameter_list),
      ')'
    ),

    _parameter_list: $ => seq(
      repeat(seq(
        choice($.identifier, $.default_parameter),
        ','
      )),
      choice(
        seq(
          choice($.identifier, $.default_parameter),
          optional(',')
        ),
        $.list_splat_parameter,
        $.dictionary_splat_parameter,
        seq(
          $.list_splat_parameter,
          ',',
          $.dictionary_splat_parameter
        )
      )
    ),

    default_parameter: $ => seq(
      $.identifier,
      '=',
      $._expression
    ),

    list_splat_parameter: $ => seq(
      '*',
      $.identifier
    ),

    dictionary_splat_parameter: $ => seq(
      '*',
      '*',
      $.identifier
    ),

    global_statement: $ => seq(
      'global',
      commaSep1($.identifier)
    ),

    class_definition: $ => seq(
      'class',
      $.identifier,
      optional(seq(
        '(',
        $.expression_list,
        ')'
      )),
      ':',
      $._suite
    ),

    decorated_definition: $ => seq(
      repeat1($.decorator),
      choice(
        $.class_definition,
        $.function_definition
      )
    ),

    decorator: $ => seq(
      '@',
      $.dotted_name,
      optional($.arguments),
      $._newline
    ),

    _suite: $ => choice(
      seq(
        $._simple_statement,
        $._newline
      ),
      seq(
        $._indent,
        repeat($._statement),
        $._dedent
      )
    ),

    arguments: $ => seq(
      '(',
      optional(
        commaSep1(
          choice($._expression, $.keyword_argument)
        )
      ),
      ')'
    ),

    variables: $ => commaSep1($._primary_expression),

    expression_list: $ => seq(
      commaSep1($._expression),
      optional(',')
    ),

    dotted_name: $ => sep1($.identifier, '.'),

    // Expressions

    _expression: $ => choice(
      $.comparison_operator,
      $.not_operator,
      $.boolean_operator,
      $.lambda,
      $._primary_expression
    ),

    _primary_expression: $ => choice(
      $.binary_operator,
      $.identifier,
      $.string,
      $.concatenated_string,
      $.number,
      $.true,
      $.false,
      $.none,
      $.unary_operator,
      $.attribute,
      $.subscript,
      $.call,
      $.list,
      $.list_comprehension,
      $.dictionary,
      $.dictionary_comprehension,
      $.set,
      $.set_comprehension,
      $.tuple,
      $.generator_expression
    ),

    not_operator: $ => choice(
      seq('not', $._expression)
    ),

    boolean_operator: $ => choice(
      prec.left(PREC.and, seq($._expression, 'and', $._expression)),
      prec.left(PREC.or, seq($._expression, 'or', $._expression))
    ),

    binary_operator: $ => choice(
      prec.left(PREC.plus, seq($._primary_expression, '+', $._primary_expression)),
      prec.left(PREC.plus, seq($._primary_expression, '-', $._primary_expression)),
      prec.left(PREC.times, seq($._primary_expression, '*', $._primary_expression)),
      prec.left(PREC.times, seq($._primary_expression, '/', $._primary_expression)),
      prec.left(PREC.times, seq($._primary_expression, '%', $._primary_expression)),
      prec.left(PREC.times, seq($._primary_expression, '//', $._primary_expression)),
      prec.left(PREC.power, seq($._primary_expression, '**', $._primary_expression)),
      prec.left(PREC.bitwise_or, seq($._primary_expression, '|', $._primary_expression)),
      prec.left(PREC.bitwise_and, seq($._primary_expression, '&', $._primary_expression)),
      prec.left(PREC.xor, seq($._primary_expression, '^', $._primary_expression)),
      prec.left(PREC.shift, seq($._primary_expression, '<<', $._primary_expression)),
      prec.left(PREC.shift, seq($._primary_expression, '>>', $._primary_expression))
    ),

    unary_operator: $ => choice(
      prec(PREC.negative, seq('-', $._primary_expression)),
      prec(PREC.negative, seq('+', $._primary_expression))
    ),

    comparison_operator: $ => prec.left(PREC.compare, seq(
      $._primary_expression,
      repeat1(seq(
        choice(
          '<',
          '<=',
          '==',
          '!=',
          '>=',
          '>',
          '<>',
          'in',
          seq('not', 'in'),
          'is',
          seq('is', 'not')
        ),
        $._primary_expression
      ))
    )),

    lambda: $ => seq(
      'lambda',
      optional($._parameter_list),
      ':',
      $._expression
    ),

    assignment: $ => seq(
      $.expression_list,
      '=',
      $._right_hand_side
    ),

    augmented_assignment: $ => seq(
      $.expression_list,
      choice('+=', '-=', '*=', '/=', '%=', '**=', '>>=', '<<=', '&=', '^=', '|='),
      $._right_hand_side
    ),

    _right_hand_side: $ => choice(
      $.expression_list,
      $.assignment,
      $.augmented_assignment,
      $.yield,
      $.conditional_expression
    ),

    yield: $ => seq('yield', optional($.expression_list)),

    attribute: $ => prec(PREC.attribute, seq(
      $._primary_expression,
      '.',
      $.identifier
    )),

    subscript: $ => prec(PREC.call, seq(
      $._primary_expression,
      '[',
      commaSep1(choice($._expression, $.slice, $.ellipsis)),
      optional(','),
      ']'
    )),

    slice: $ => seq(
      optional($._expression),
      ':',
      optional($._expression),
      optional(seq(':', $._expression))
    ),

    ellipsis: $ => '...',

    call: $ => prec(PREC.call, seq(
      $._primary_expression,
      choice(
        $.generator_expression,
        seq(
          '(',
          repeat(seq(
            choice($._expression, $.keyword_argument),
            ','
          )),
          optional(choice(
            seq(
              choice($._expression, $.keyword_argument),
              optional(',')
            ),
            seq(
              $.list_splat_argument,
              repeat(seq(',', choice($._expression, $.keyword_argument))),
              optional(seq(',', $.dictionary_splat_argument))
            ),
            $.dictionary_splat_argument
          )),
          ')'
        )
      )
    )),

    keyword_argument: $ => seq(
      $.identifier,
      '=',
      $._expression
    ),

    list_splat_argument: $ => seq(
      '*',
      $._expression
    ),

    dictionary_splat_argument: $ => seq(
      '**',
      $._expression
    ),

    // Literals

    list: $ => seq(
      '[',
      optional(seq(
        commaSep1($._expression),
        optional(',')
      )),
      ']'
    ),

    list_comprehension: $ => seq(
      '[',
      $._expression,
      'for',
      $.variables,
      'in',
      $._expression,
      ']'
    ),

    dictionary: $ => seq(
      '{',
      optional(seq(
        commaSep1($.pair),
        optional(',')
      )),
      '}'
    ),

    dictionary_comprehension: $ => seq(
      '{',
      $.pair,
      'for',
      $.variables,
      'in',
      $._expression,
      '}'
    ),

    pair: $ => seq(
      $._expression,
      ':',
      $._expression
    ),

    set: $ => seq(
      '{',
      seq(
        commaSep1($._expression),
        optional(',')
      ),
      '}'
    ),

    set_comprehension: $ => seq(
      '{',
      $._expression,
      'for',
      $.variables,
      'in',
      $._expression,
      '}'
    ),

    tuple: $ => seq(
      '(',
      optional(seq(
        commaSep1($._expression),
        optional(',')
      )),
      ')'
    ),

    generator_expression: $ => seq(
      '(',
      $._expression,
      'for',
      $.variables,
      'in',
      $._expression,
      ')'
    ),

    conditional_expression: $ => prec.right(PREC.conditional, seq(
      choice($.assignment, $._expression),
      'if',
      $._expression,
      optional(seq(
        'else',
        $._expression
      )))
    ),

    concatenated_string: $ => seq(
      $.string,
      repeat1($.string)
    ),

    string: $ => token(seq(
      repeat(choice('u', 'r', 'b')),
      choice(
        seq(
          '"',
          repeat(choice(
            /[^\\"]/,
            /\\./
          )),
          '"'
        ),
        seq(
          "'",
          repeat(choice(
            /[^\\']/,
            /\\./
          )),
          "'"
        ),
        seq(
          '"""',
          repeat(choice(
            /[^"]/,
            /"[^"]/,
            /""[^"]/
          )),
          '"""'
        ),
        seq(
          "'''",
          repeat(choice(
            /[^']/,
            /"[^']/,
            /""[^']/
          )),
          "'''"
        )
      )
    )),

    number: $ => token(choice(
      seq(
        '0x',
        /[A-Fa-f0-9]+/
      ),
      seq(
        '.',
        /\d+/
      ),
      seq(
        /\d+/,
        optional(seq(
          '.',
          /\d*/
        )),
        optional(/[eE][\+-]?/),
        /\d*/
      )
    )),

    identifier: $ => /[\a_]\w*/,

    true: $ => 'True',
    false: $ => 'False',
    none: $ => 'None',

    comment: $ => token(seq('#', /.*/))
  }
})

function commaSep1 (rule) {
  return sep1(rule, ',')
}

function sep1 (rule, separator) {
  return seq(rule, repeat(seq(separator, rule)))
}
<commit_msg>Update conditional_expression to not contain assignment<commit_after>const PREC = {
  compare: 2,
  conditional: 2,
  or: 10,
  and: 11,
  bitwise_or: 12,
  bitwise_and: 13,
  xor: 14,
  shift: 15,
  plus: 16,
  times: 17,
  power: 18,
  not: 1,
  negative: 19,
  call: 20,
  attribute: 20
}

module.exports = grammar({
  name: 'python',

  extras: $ => [
    $.comment,
    /\s|\\\n/
  ],

  externals: $ => [
    $._newline,
    $._indent,
    $._dedent
  ],

  rules: {
    module: $ => repeat($._statement),

    _statement: $ => choice(
      seq($._simple_statement, repeat1($._newline)),
      $._compound_statement
    ),

    // Simple statements

    _simple_statement: $ => choice(
      $.import_statement,
      $.import_from_statement,
      $.print_statement,
      $.assert_statement,
      $.expression_statement,
      $.return_statement,
      $.delete_statement,
      $.raise_statement,
      $.pass_statement,
      $.break_statement,
      $.continue_statement,
      $.global_statement
    ),

    import_statement: $ => seq(
      'import',
      $._import_list
    ),

    import_from_statement: $ => seq(
      'from',
      choice(
        seq(
          repeat('.'),
          $.dotted_name
        ),
        repeat1('.')
      ),
      'import',
      choice(
        $.wildcard_import,
        $._import_list,
        seq('(', $._import_list, ')')
      )
    ),

    _import_list: $ => seq(
      commaSep1(choice(
        $.dotted_name,
        $.aliased_import
      )),
      optional(',')
    ),

    aliased_import: $ => seq(
      $.dotted_name,
      'as',
      $.identifier
    ),

    wildcard_import: $ => '*',

    print_statement: $ => seq(
      'print',
      $._expression,
      repeat(seq(',', $._expression))
    ),

    assert_statement: $ => seq(
      'assert',
      $._expression,
      repeat(seq(',', $._expression))
    ),

    expression_statement: $ => choice(
      $._expression,
      $.assignment,
      $.augmented_assignment,
      $.yield,
      $.conditional_expression
    ),

    return_statement: $ => seq(
      'return',
      optional($.expression_list)
    ),

    delete_statement: $ => seq(
      'del',
      $.expression_list
    ),

    raise_statement: $ => seq(
      'raise',
      optional($.expression_list)
    ),

    pass_statement: $ => 'pass',
    break_statement: $ => 'break',
    continue_statement: $ => 'continue',

    // Compount statements

    _compound_statement: $ => choice(
      $.if_statement,
      $.for_statement,
      $.while_statement,
      $.try_statement,
      $.with_statement,
      $.function_definition,
      $.class_definition,
      $.decorated_definition
    ),

    if_statement: $ => seq(
      'if',
      $._expression,
      ':',
      $._suite,
      repeat($.elif_clause),
      optional($.else_clause)
    ),

    elif_clause: $ => seq(
      'elif',
      $._expression,
      ':',
      $._suite
    ),

    else_clause: $ => seq(
      'else',
      ':',
      $._suite
    ),

    for_statement: $ => seq(
      'for',
      $.variables,
      'in',
      $.expression_list,
      ':',
      $._suite,
      optional($.else_clause)
    ),

    while_statement: $ => seq(
      'while',
      $._expression,
      ':',
      $._suite,
      optional($.else_clause)
    ),

    try_statement: $ => seq(
      'try',
      ':',
      $._suite,
      choice(
        seq(
          repeat1($.except_clause),
          optional($.else_clause),
          optional($.finally_clause)
        ),
        $.finally_clause
      )
    ),

    except_clause: $ => seq(
      'except',
      optional(seq(
        $._expression,
        optional(seq(
          choice('as', ','),
          $._expression
        ))
      )),
      ':',
      $._suite
    ),

    finally_clause: $ => seq(
      'finally',
      ':',
      $._suite
    ),

    with_statement: $ => seq(
      'with',
      commaSep1($.with_item),
      ':',
      $._suite
    ),

    with_item: $ => seq(
      $._expression,
      optional(seq(
        'as',
        $._expression
      ))
    ),

    function_definition: $ => seq(
      'def',
      $.identifier,
      $.parameters,
      ':',
      $._suite
    ),

    parameters: $ => seq(
      '(',
      optional($._parameter_list),
      ')'
    ),

    _parameter_list: $ => seq(
      repeat(seq(
        choice($.identifier, $.default_parameter),
        ','
      )),
      choice(
        seq(
          choice($.identifier, $.default_parameter),
          optional(',')
        ),
        $.list_splat_parameter,
        $.dictionary_splat_parameter,
        seq(
          $.list_splat_parameter,
          ',',
          $.dictionary_splat_parameter
        )
      )
    ),

    default_parameter: $ => seq(
      $.identifier,
      '=',
      $._expression
    ),

    list_splat_parameter: $ => seq(
      '*',
      $.identifier
    ),

    dictionary_splat_parameter: $ => seq(
      '*',
      '*',
      $.identifier
    ),

    global_statement: $ => seq(
      'global',
      commaSep1($.identifier)
    ),

    class_definition: $ => seq(
      'class',
      $.identifier,
      optional(seq(
        '(',
        $.expression_list,
        ')'
      )),
      ':',
      $._suite
    ),

    decorated_definition: $ => seq(
      repeat1($.decorator),
      choice(
        $.class_definition,
        $.function_definition
      )
    ),

    decorator: $ => seq(
      '@',
      $.dotted_name,
      optional($.arguments),
      $._newline
    ),

    _suite: $ => choice(
      seq(
        $._simple_statement,
        $._newline
      ),
      seq(
        $._indent,
        repeat($._statement),
        $._dedent
      )
    ),

    arguments: $ => seq(
      '(',
      optional(
        commaSep1(
          choice($._expression, $.keyword_argument)
        )
      ),
      ')'
    ),

    variables: $ => commaSep1($._primary_expression),

    expression_list: $ => seq(
      commaSep1($._expression),
      optional(',')
    ),

    dotted_name: $ => sep1($.identifier, '.'),

    // Expressions

    _expression: $ => choice(
      $.comparison_operator,
      $.not_operator,
      $.boolean_operator,
      $.lambda,
      $._primary_expression
    ),

    _primary_expression: $ => choice(
      $.binary_operator,
      $.identifier,
      $.string,
      $.concatenated_string,
      $.number,
      $.true,
      $.false,
      $.none,
      $.unary_operator,
      $.attribute,
      $.subscript,
      $.call,
      $.list,
      $.list_comprehension,
      $.dictionary,
      $.dictionary_comprehension,
      $.set,
      $.set_comprehension,
      $.tuple,
      $.generator_expression
    ),

    not_operator: $ => choice(
      seq('not', $._expression)
    ),

    boolean_operator: $ => choice(
      prec.left(PREC.and, seq($._expression, 'and', $._expression)),
      prec.left(PREC.or, seq($._expression, 'or', $._expression))
    ),

    binary_operator: $ => choice(
      prec.left(PREC.plus, seq($._primary_expression, '+', $._primary_expression)),
      prec.left(PREC.plus, seq($._primary_expression, '-', $._primary_expression)),
      prec.left(PREC.times, seq($._primary_expression, '*', $._primary_expression)),
      prec.left(PREC.times, seq($._primary_expression, '/', $._primary_expression)),
      prec.left(PREC.times, seq($._primary_expression, '%', $._primary_expression)),
      prec.left(PREC.times, seq($._primary_expression, '//', $._primary_expression)),
      prec.left(PREC.power, seq($._primary_expression, '**', $._primary_expression)),
      prec.left(PREC.bitwise_or, seq($._primary_expression, '|', $._primary_expression)),
      prec.left(PREC.bitwise_and, seq($._primary_expression, '&', $._primary_expression)),
      prec.left(PREC.xor, seq($._primary_expression, '^', $._primary_expression)),
      prec.left(PREC.shift, seq($._primary_expression, '<<', $._primary_expression)),
      prec.left(PREC.shift, seq($._primary_expression, '>>', $._primary_expression))
    ),

    unary_operator: $ => choice(
      prec(PREC.negative, seq('-', $._primary_expression)),
      prec(PREC.negative, seq('+', $._primary_expression))
    ),

    comparison_operator: $ => prec.left(PREC.compare, seq(
      $._primary_expression,
      repeat1(seq(
        choice(
          '<',
          '<=',
          '==',
          '!=',
          '>=',
          '>',
          '<>',
          'in',
          seq('not', 'in'),
          'is',
          seq('is', 'not')
        ),
        $._primary_expression
      ))
    )),

    lambda: $ => seq(
      'lambda',
      optional($._parameter_list),
      ':',
      $._expression
    ),

    assignment: $ => seq(
      $.expression_list,
      '=',
      $._right_hand_side
    ),

    augmented_assignment: $ => seq(
      $.expression_list,
      choice('+=', '-=', '*=', '/=', '%=', '**=', '>>=', '<<=', '&=', '^=', '|='),
      $._right_hand_side
    ),

    _right_hand_side: $ => choice(
      $.expression_list,
      $.assignment,
      $.augmented_assignment,
      $.yield,
      $.conditional_expression
    ),

    yield: $ => seq('yield', optional($.expression_list)),

    attribute: $ => prec(PREC.attribute, seq(
      $._primary_expression,
      '.',
      $.identifier
    )),

    subscript: $ => prec(PREC.call, seq(
      $._primary_expression,
      '[',
      commaSep1(choice($._expression, $.slice, $.ellipsis)),
      optional(','),
      ']'
    )),

    slice: $ => seq(
      optional($._expression),
      ':',
      optional($._expression),
      optional(seq(':', $._expression))
    ),

    ellipsis: $ => '...',

    call: $ => prec(PREC.call, seq(
      $._primary_expression,
      choice(
        $.generator_expression,
        seq(
          '(',
          repeat(seq(
            choice($._expression, $.keyword_argument),
            ','
          )),
          optional(choice(
            seq(
              choice($._expression, $.keyword_argument),
              optional(',')
            ),
            seq(
              $.list_splat_argument,
              repeat(seq(',', choice($._expression, $.keyword_argument))),
              optional(seq(',', $.dictionary_splat_argument))
            ),
            $.dictionary_splat_argument
          )),
          ')'
        )
      )
    )),

    keyword_argument: $ => seq(
      $.identifier,
      '=',
      $._expression
    ),

    list_splat_argument: $ => seq(
      '*',
      $._expression
    ),

    dictionary_splat_argument: $ => seq(
      '**',
      $._expression
    ),

    // Literals

    list: $ => seq(
      '[',
      optional(seq(
        commaSep1($._expression),
        optional(',')
      )),
      ']'
    ),

    list_comprehension: $ => seq(
      '[',
      $._expression,
      'for',
      $.variables,
      'in',
      $._expression,
      ']'
    ),

    dictionary: $ => seq(
      '{',
      optional(seq(
        commaSep1($.pair),
        optional(',')
      )),
      '}'
    ),

    dictionary_comprehension: $ => seq(
      '{',
      $.pair,
      'for',
      $.variables,
      'in',
      $._expression,
      '}'
    ),

    pair: $ => seq(
      $._expression,
      ':',
      $._expression
    ),

    set: $ => seq(
      '{',
      seq(
        commaSep1($._expression),
        optional(',')
      ),
      '}'
    ),

    set_comprehension: $ => seq(
      '{',
      $._expression,
      'for',
      $.variables,
      'in',
      $._expression,
      '}'
    ),

    tuple: $ => seq(
      '(',
      optional(seq(
        commaSep1($._expression),
        optional(',')
      )),
      ')'
    ),

    generator_expression: $ => seq(
      '(',
      $._expression,
      'for',
      $.variables,
      'in',
      $._expression,
      ')'
    ),

    conditional_expression: $ => prec.right(PREC.conditional, seq(
      $._expression,
      'if',
      $._expression,
      optional(seq(
        'else',
        $._expression
      )))
    ),

    concatenated_string: $ => seq(
      $.string,
      repeat1($.string)
    ),

    string: $ => token(seq(
      repeat(choice('u', 'r', 'b')),
      choice(
        seq(
          '"',
          repeat(choice(
            /[^\\"]/,
            /\\./
          )),
          '"'
        ),
        seq(
          "'",
          repeat(choice(
            /[^\\']/,
            /\\./
          )),
          "'"
        ),
        seq(
          '"""',
          repeat(choice(
            /[^"]/,
            /"[^"]/,
            /""[^"]/
          )),
          '"""'
        ),
        seq(
          "'''",
          repeat(choice(
            /[^']/,
            /"[^']/,
            /""[^']/
          )),
          "'''"
        )
      )
    )),

    number: $ => token(choice(
      seq(
        '0x',
        /[A-Fa-f0-9]+/
      ),
      seq(
        '.',
        /\d+/
      ),
      seq(
        /\d+/,
        optional(seq(
          '.',
          /\d*/
        )),
        optional(/[eE][\+-]?/),
        /\d*/
      )
    )),

    identifier: $ => /[\a_]\w*/,

    true: $ => 'True',
    false: $ => 'False',
    none: $ => 'None',

    comment: $ => token(seq('#', /.*/))
  }
})

function commaSep1 (rule) {
  return sep1(rule, ',')
}

function sep1 (rule, separator) {
  return seq(rule, repeat(seq(separator, rule)))
}
<|endoftext|>


<commit_before>console.time('init')

var electron = require('electron')

var app = electron.app
var ipcMain = electron.ipcMain

var config = require('../config')
var crashReporter = require('../crash-reporter')
var handlers = require('./handlers')
var ipc = require('./ipc')
var log = require('./log')
var menu = require('./menu')
var shortcuts = require('./shortcuts')
var squirrelWin32 = require('./squirrel-win32')
var tray = require('./tray')
var updater = require('./updater')
var windows = require('./windows')

var shouldQuit = false
var argv = sliceArgv(process.argv)

if (process.platform === 'win32') {
  shouldQuit = squirrelWin32.handleEvent(argv[0])
  argv = argv.filter((arg) => arg.indexOf('--squirrel') === -1)
}

if (!shouldQuit) {
  // Prevent multiple instances of app from running at same time. New instances signal
  // this instance and quit.
  shouldQuit = app.makeSingleInstance(onAppOpen)
  if (shouldQuit) {
    app.quit()
  }
}

if (!shouldQuit) {
  init()
}

function init () {
  if (config.IS_PORTABLE) {
    app.setPath('userData', config.CONFIG_PATH)
  }

  app.ipcReady = false // main window has finished loading and IPC is ready
  app.isQuitting = false

  // Open handlers must be added as early as possible
  app.on('open-file', onOpen)
  app.on('open-url', onOpen)

  ipc.init()

  app.on('will-finish-launching', function () {
    crashReporter.init()
  })

  app.on('ready', function () {
    windows.createMainWindow()
    windows.createWebTorrentHiddenWindow()
    menu.init()
    shortcuts.init()

    // To keep app startup fast, some code is delayed.
    setTimeout(delayedInit, config.DELAYED_INIT)
  })

  app.on('ipcReady', function () {
    log('Command line args:', argv)
    processArgv(argv)
    console.timeEnd('init')
  })

  app.on('before-quit', function (e) {
    if (app.isQuitting) return

    app.isQuitting = true
    e.preventDefault()
    windows.main.send('dispatch', 'saveState') /* try to save state on exit */
    ipcMain.once('savedState', () => app.quit())
    setTimeout(() => app.quit(), 2000) /* quit after 2 secs, at most */
  })

  app.on('activate', function () {
    windows.createMainWindow()
  })
}

function delayedInit () {
  tray.init()
  handlers.install()
  updater.init()
}

function onOpen (e, torrentId) {
  e.preventDefault()

  if (app.ipcReady) {
    windows.main.send('dispatch', 'onOpen', torrentId)
    // Magnet links opened from Chrome won't focus the app without a setTimeout. The
    // confirmation dialog Chrome shows causes Chrome to steal back the focus.
    // Electron issue: https://github.com/atom/electron/issues/4338
    setTimeout(function () {
      windows.focusWindow(windows.main)
    }, 100)
  } else {
    argv.push(torrentId)
  }
}

function onAppOpen (newArgv) {
  newArgv = sliceArgv(newArgv)

  if (app.ipcReady) {
    log('Second app instance opened, but was prevented:', newArgv)
    windows.focusWindow(windows.main)

    processArgv(newArgv)
  } else {
    argv.push(...newArgv)
  }
}

function sliceArgv (argv) {
  return argv.slice(config.IS_PRODUCTION ? 1 : 2)
}

function processArgv (argv) {
  argv.forEach(function (arg) {
    if (arg === '-n') {
      menu.showOpenSeedFiles()
    } else if (arg === '-o') {
      menu.showOpenTorrentFile()
    } else if (arg === '-u') {
      menu.showOpenTorrentAddress()
    } else if (arg.startsWith('-psn')) {
      // Ignore OS X launchd "process serial number" argument
      // More: https://github.com/feross/webtorrent-desktop/issues/214
    } else {
      windows.main.send('dispatch', 'onOpen', arg)
    }
  })
}
<commit_msg>Wait for the app ready event before creating windows<commit_after>console.time('init')

var electron = require('electron')

var app = electron.app
var ipcMain = electron.ipcMain

var config = require('../config')
var crashReporter = require('../crash-reporter')
var handlers = require('./handlers')
var ipc = require('./ipc')
var log = require('./log')
var menu = require('./menu')
var shortcuts = require('./shortcuts')
var squirrelWin32 = require('./squirrel-win32')
var tray = require('./tray')
var updater = require('./updater')
var windows = require('./windows')

var shouldQuit = false
var argv = sliceArgv(process.argv)

if (process.platform === 'win32') {
  shouldQuit = squirrelWin32.handleEvent(argv[0])
  argv = argv.filter((arg) => arg.indexOf('--squirrel') === -1)
}

if (!shouldQuit) {
  // Prevent multiple instances of app from running at same time. New instances signal
  // this instance and quit.
  shouldQuit = app.makeSingleInstance(onAppOpen)
  if (shouldQuit) {
    app.quit()
  }
}

if (!shouldQuit) {
  init()
}

function init () {
  if (config.IS_PORTABLE) {
    app.setPath('userData', config.CONFIG_PATH)
  }

  app.isReady = false // app ready, windows can be created
  app.ipcReady = false // main window has finished loading and IPC is ready
  app.isQuitting = false

  // Open handlers must be added as early as possible
  app.on('open-file', onOpen)
  app.on('open-url', onOpen)

  ipc.init()

  app.on('will-finish-launching', function () {
    crashReporter.init()
  })

  app.on('ready', function () {
    app.isReady = true

    windows.createMainWindow()
    windows.createWebTorrentHiddenWindow()
    menu.init()
    shortcuts.init()

    // To keep app startup fast, some code is delayed.
    setTimeout(delayedInit, config.DELAYED_INIT)
  })

  app.on('ipcReady', function () {
    log('Command line args:', argv)
    processArgv(argv)
    console.timeEnd('init')
  })

  app.on('before-quit', function (e) {
    if (app.isQuitting) return

    app.isQuitting = true
    e.preventDefault()
    windows.main.send('dispatch', 'saveState') /* try to save state on exit */
    ipcMain.once('savedState', () => app.quit())
    setTimeout(() => app.quit(), 2000) /* quit after 2 secs, at most */
  })

  app.on('activate', function () {
    if (app.isReady) windows.createMainWindow()
  })
}

function delayedInit () {
  tray.init()
  handlers.install()
  updater.init()
}

function onOpen (e, torrentId) {
  e.preventDefault()

  if (app.ipcReady) {
    windows.main.send('dispatch', 'onOpen', torrentId)
    // Magnet links opened from Chrome won't focus the app without a setTimeout. The
    // confirmation dialog Chrome shows causes Chrome to steal back the focus.
    // Electron issue: https://github.com/atom/electron/issues/4338
    setTimeout(function () {
      windows.focusWindow(windows.main)
    }, 100)
  } else {
    argv.push(torrentId)
  }
}

function onAppOpen (newArgv) {
  newArgv = sliceArgv(newArgv)

  if (app.ipcReady) {
    log('Second app instance opened, but was prevented:', newArgv)
    windows.focusWindow(windows.main)

    processArgv(newArgv)
  } else {
    argv.push(...newArgv)
  }
}

function sliceArgv (argv) {
  return argv.slice(config.IS_PRODUCTION ? 1 : 2)
}

function processArgv (argv) {
  argv.forEach(function (arg) {
    if (arg === '-n') {
      menu.showOpenSeedFiles()
    } else if (arg === '-o') {
      menu.showOpenTorrentFile()
    } else if (arg === '-u') {
      menu.showOpenTorrentAddress()
    } else if (arg.startsWith('-psn')) {
      // Ignore OS X launchd "process serial number" argument
      // More: https://github.com/feross/webtorrent-desktop/issues/214
    } else {
      windows.main.send('dispatch', 'onOpen', arg)
    }
  })
}
<|endoftext|>


<commit_before>/*
 * Copyrights phwang
 * Written by Paul Hwang
 * File name: go_container_module.js
 */

module.exports = {
    malloc: function (cluster_object_val) {
        return new goContainerObject(cluster_object_val);
    },
};

function goContainerObject (cluster_object_val) {
    "use strict";
    this.theUtilModule = require("./../util_modules/util_module.js");
    this.theClusterModule = require("./../fibre_modules/cluster_module.js");
    this.theSessionModule = require("./../fibre_modules/session_entry_module.js");
    this.theConfigModule = require("./go_config_module.js");
    this.theGameModule = require("./go_game_module.js");
    this.theEngineModule = require("./go_engine_module.js");
    this.theBoardModule = require("./go_board_module.js");
    this.theHandlerModule = require("./go_handler_module.js");
    this.thePortModule = require("./go_port_module.js");
    this.theGoDefineModule = require("./go_define_module.js");

    this.theClusterObject = cluster_object_val;

    this.utilModule = function () {
        return this.theUtilModule;
    };

    this.clusterModule = function () {
        return this.theClusterModule;
    };

    this.handlerModule = function () {
        return this.theHandlerModule;
    };

    this.portModule = function () {
        return this.thePortModule;
    };

    this.sessionModule = function () {
        return this.theSessionModule;
    };

    this.configModule = function () {
        return this.theConfigModule;
    };

    this.gameModule = function () {
        return this.theGameModule;
    };

    this.engineModule = function () {
        return this.theEngineModule;
    };

    this.boardModule = function () {
        return this.theBoardModule;
    };

    this.mallocObjects = function () {
        this.theConfigObject = this.configModule().malloc(this);
        this.theBoardObject = this.boardModule().malloc(this);
        this.theEngineObject = this.engineModule().malloc(this);
        this.theGameObject = this.gameModule().malloc(this);
        this.theHandlerObject = this.handlerModule().malloc(this);
        this.thePortObject = this.portModule().malloc(this);
    }

    this.objectName = function () {
        return "GoContainerObject";
    };

    this.GO = function () {
        return this.theGoDefineModule;
    };

    this.clusterObject = function () {
        return this.theClusterObject;
    };

    this.rootObject = function () {
        return this.sessionObject().rootObject();
    };

    this.configObject = function () {
        return this.theConfigObject;
    };

    this.uiObject = function () {
        return this.theUiObject;
    };

    this.containerIndex = function () {
        return this.theContainerIndex;
    };

    this.utilObject = function () {
        return this.rootObject().utilObject();
    };

    this.boardObject = function () {
        return this.theBoardObject;
    };

    this.engineObject = function () {
        return this.theEngineObject;
    };

    this.gameObject = function () {
        return this.theGameObject;
    };

    this.handlerObject = function () {
        return this.theHandlerObject;
    };

    this.portObject = function () {
        return this.thePortObject;
    };

    this.containerObject2 = function () {
        return this.theConfigObject2;
    };

    this.setHisContainerObject = function (container2_val) {
        this.theConfigObject2 = container2_val;
    };

    this.lastGame = function () {
        return window.localStorage.lastGame;
    }

    this.setLastGame = function (str_val) {
        window.localStorage.lastGame = str_val;
    }

    this.resetContainerObjectForNewGame = function () {
        this.goLog("resetContainerObjectForNewGame", "");
        this.setLastGame(0);
        this.gameObject().resetGameObjectData();
        this.boardObject().resetBoardObjectData();
        this.engineObject().resetEngineObjectData();
    };

    this.abend = function (str1_val, str2_val) {
        return this.goAbend(this.objectName() + "." + str1_val, str2_val);
    };

    this.logit = function (str1_val, str2_val) {
        return this.goLogit(this.objectName() + "." + str1_val, str2_val);
    };

    this.goLog = function (s1_val, s2_val) {
        if (this.clusterObject().sessionArrayLength() === 1) {
            this.utilModule().utilLogit(this.clusterObject().sessionArray(0).sessionId() + "<=>" +
                                        this.clusterObject().sessionArray(0).sessionId() + " " + s1_val, s2_val);
        }
        else {
            this.utilModule().utilLogit(this.clusterObject().sessionArray(0).sessionId() + "<=>" +
                                        this.clusterObject().sessionArray(1).sessionId() + " " + s1_val, s2_val);
        }
    };

    this.goAbend = function (s1_val, s2_val) {
        if (this.topicObject().sessionArrayLength() === 1) {
            this.utilModule().utilAbend(this.topicObject().sessionArray(0).sessionId() + "<=>" +
                                        this.topicObject().sessionArray(0).sessionId() + " " + s1_val, s2_val);
        }
        else {
            this.utilModule().utilAbend(this.topicObject().sessionArray(0).sessionId() + "<=>" +
                                        this.topicObject().sessionArray(1).sessionId() + " " + s1_val, s2_val);
        }
    };

    this.startGoGame = function () {
        this.gameObject().processTheWholeMoveList();
        this.sessionObject().setupClientReceiveCallback(function (container_val, data_val) {
            //console.log("ajaxReceiveCallBack" + port_val.objectName());
            container_val.portObject().receiveStringData(data_val);
        }, this);
    };

    this.runGoGame = function () {
        var this0 = this;
        this.startGoGame();
        this.rootObject().htmlObject().createPlayHolders();
        this.uiObject().initElements();
        this.uiObject().drawBoard(this.engineObject());

        $("canvas").on("click", function(event) {
            this0.uiObject().uiClickApi(event.clientX, event.clientY);
        });

        $("canvas").on("mousemove", function(event) {
            this0.uiObject().uiMouseMove(event.clientX, event.clientY);
        });

        var addCommentFromInputBox = function () {
            var $new_comment = $("<p>");
            if ($(".comment-input input").val() !== "") {
                $new_comment.text($(".comment-input input").val());
                $new_comment.hide();
                $(".comments").append($new_comment);
                $new_comment.fadeIn();
                $(".comment-input input").val("");
            }
        };

        $(".comment-input button").on("click", function(event) {
            addCommentFromInputBox();
        });

        $(".comment-input input").on("keypress", function(event) {
            if (event.keyCode == 13) {
                addCommentFromInputBox();
            }
        });
    };

    this.mallocObjects();
}

var GO = new GoDefineObject;
function GoDefineObject() {
    this.objectName = "GoDefineObject";

    this.THE_FORWARD_MOVE = "FORWARD";
    this.THE_BACKWARD_MOVE = "BACKWARD";
    this.THE_DOUBLE_FORWARD_MOVE = "DOUBLE_FORWARD";
    this.THE_DOUBLE_BACKWARD_MOVE = "DOUBLE_BACKWARD";
    this.THE_PASS_MOVE = "PASS";
    this.THE_RESIGN_MOVE = "RESIGN";
    this.THE_BACK_TO_PLAY_MOVE = "BACK_TO_PLAY";
    this.THE_CONFIRM_MOVE = "CONFIRM";
    this.THE_PLAY_ANOTHER_GAME_MOVE = "PLAY_ANOTHER_GAME";

    this.THE_EMPTY_STONE = 0;
    this.THE_BLACK_STONE = 1;
    this.THE_WHITE_STONE = 2;
    this.THE_BOTH_COLOR_STONE = 2;
    this.THE_MARK_DEAD_STONE_DIFF = 4;
    this.THE_MARK_EMPTY_STONE_DIFF = 6;

    this.THE_MARKED_DEAD_BLACK_STONE  = (this.THE_BLACK_STONE + this.THE_MARK_DEAD_STONE_DIFF);
    this.THE_MARKED_DEAD_WHITE_STONE  = (this.THE_WHITE_STONE + this.THE_MARK_DEAD_STONE_DIFF);
    this.THE_MARKED_EMPTY_BLACK_STONE = (this.THE_BLACK_STONE + this.THE_MARK_EMPTY_STONE_DIFF);
    this.THE_MARKED_EMPTY_WHITE_STONE = (this.THE_WHITE_STONE + this.THE_MARK_EMPTY_STONE_DIFF);

    //this.X_Y_VALUE_FOR_PASS = 99;

    this.FORWARD_MOVE = function () {return this.THE_FORWARD_MOVE;};
    this.BACKWARD_MOVE = function () {return this.THE_BACKWARD_MOVE;};
    this.DOUBLE_FORWARD_MOVE = function () {return this.THE_DOUBLE_FORWARD_MOVE;};
    this.DOUBLE_BACKWARD_MOVE = function () {return this.THE_DOUBLE_BACKWARD_MOVE;};
    this.PASS_MOVE = function () {return this.THE_PASS_MOVE;};
    this.RESIGN_MOVE = function () {return this.THE_RESIGN_MOVE;};
    this.BACK_TO_PLAY_MOVE = function () {return this.THE_BACK_TO_PLAY_MOVE;};
    this.CONFIRM_MOVE = function () {return this.THE_CONFIRM_MOVE;};
    this.PLAY_ANOTHER_GAME_MOVE = function () {return this.THE_PLAY_ANOTHER_GAME_MOVE;};

    this.EMPTY_STONE = function () {return this.THE_EMPTY_STONE;};
    this.BLACK_STONE = function () {return this.THE_BLACK_STONE;};
    this.WHITE_STONE = function () {return this.THE_WHITE_STONE;};
    this.BOTH_COLOR_STONE = function () {return this.THE_BOTH_COLOR_STONE;};
    this.MARK_DEAD_STONE_DIFF = function () {return this.THE_MARK_DEAD_STONE_DIFF;};
    this.MARK_EMPTY_STONE_DIFF = function () {return this.THE_MARK_EMPTY_STONE_DIFF;};

    this.MARKED_DEAD_BLACK_STONE = function () {return this.THE_MARKED_DEAD_BLACK_STONE;};
    this.MARKED_DEAD_WHITE_STONE = function () {return this.THE_MARKED_DEAD_WHITE_STONE;};
    this.MARKED_EMPTY_BLACK_STONE = function () {return this.THE_MARKED_EMPTY_BLACK_STONE;};
    this.MARKED_EMPTY_WHITE_STONE = function () {return this.THE_MARKED_EMPTY_WHITE_STONE;};

//    this.X_Y_VALUE_FOR_PASS_ = function () {return this.X_Y_VALUE_FOR_PASS;}

    this.getOppositeColor = function (color_val) {
        switch (color_val) {
        case GO.BLACK_STONE():
            return GO.WHITE_STONE();

        case GO.WHITE_STONE():
            return GO.BLACK_STONE();

        default:
            this.goAbend("getOppositeColor", "color=" + color_val);
            return GO.EMPTY_STONE();
        }
    };

    this.isNeighborStone = function (x1_val, y1_val, x2_val, y2_val) {
        if (x1_val === x2_val) {
            if ((y1_val + 1 === y2_val) || (y1_val - 1 === y2_val)) {
                return true;
            }
        }
        if (y1_val === y2_val) {
            if ((x1_val + 1 === x2_val) || (x1_val - 1 === x2_val)) {
                return true;
            }
        }
        return false;
    };

    this.isValidCoordinates = function (x_val, y_val, board_size_val) {
        return this.isValidCoordinate(x_val, board_size_val) && this.isValidCoordinate(y_val, board_size_val) ;
    };

    this.isValidCoordinate = function (coordinate_val, board_size_val) {
        return (0 <= coordinate_val) && (coordinate_val < board_size_val);
    };

    this.goLog = function (s1_val, s2_val) {
        window.console.log(s1_val + "() " + s2_val);
    };

    this.goAbend = function (s1_val, s2_val) {
        window.console.log("goAbend: " + s1_val + "() " + s2_val);
        window.alert("Abend: " + s1_val + "() " + s2_val);
        var x = junk;
    };
}
<commit_msg>container modules<commit_after>/*
 * Copyrights phwang
 * Written by Paul Hwang
 * File name: go_container_module.js
 */

module.exports = {
    malloc: function (cluster_object_val) {
        return new goContainerObject(cluster_object_val);
    },
};

function goContainerObject (cluster_object_val) {
    "use strict";
    this.theUtilModule = require("./../util_modules/util_module.js");
    this.theClusterModule = require("./../fibre_modules/cluster_module.js");
    this.theSessionModule = require("./../fibre_modules/session_entry_module.js");
    this.theGoDefineModule = require("./go_define_module.js");

    this.theClusterObject = cluster_object_val;

    this.utilModule = function () {
        return this.theUtilModule;
    };

    this.clusterModule = function () {
        return this.theClusterModule;
    };

    this.sessionModule = function () {
        return this.theSessionModule;
    };

    this.GO = function () {
        return this.theGoDefineModule;
    };

    this.mallocObjects = function () {
        var config_module = require("./go_config_module.js");
        var board_module = require("./go_board_module.js");
        var engine_module = require("./go_engine_module.js");
        var game_module = require("./go_game_module.js");
        var handler_module = require("./go_handler_module.js");
        var port_module = require("./go_port_module.js");

        this.theConfigObject = config_module.malloc(this);
        this.theBoardObject = board_module.malloc(this);
        this.theEngineObject = engine_module.malloc(this);
        this.theGameObject = game_module.malloc(this);
        this.theHandlerObject = handler_module.malloc(this);
        this.thePortObject = port_module.malloc(this);
    }

    this.objectName = function () {
        return "GoContainerObject";
    };

    this.clusterObject = function () {
        return this.theClusterObject;
    };

    this.rootObject = function () {
        return this.sessionObject().rootObject();
    };

    this.configObject = function () {
        return this.theConfigObject;
    };

    this.uiObject = function () {
        return this.theUiObject;
    };

    this.containerIndex = function () {
        return this.theContainerIndex;
    };

    this.utilObject = function () {
        return this.rootObject().utilObject();
    };

    this.boardObject = function () {
        return this.theBoardObject;
    };

    this.engineObject = function () {
        return this.theEngineObject;
    };

    this.gameObject = function () {
        return this.theGameObject;
    };

    this.handlerObject = function () {
        return this.theHandlerObject;
    };

    this.portObject = function () {
        return this.thePortObject;
    };

    this.containerObject2 = function () {
        return this.theConfigObject2;
    };

    this.setHisContainerObject = function (container2_val) {
        this.theConfigObject2 = container2_val;
    };

    this.lastGame = function () {
        return window.localStorage.lastGame;
    }

    this.setLastGame = function (str_val) {
        window.localStorage.lastGame = str_val;
    }

    this.resetContainerObjectForNewGame = function () {
        this.goLog("resetContainerObjectForNewGame", "");
        this.setLastGame(0);
        this.gameObject().resetGameObjectData();
        this.boardObject().resetBoardObjectData();
        this.engineObject().resetEngineObjectData();
    };

    this.abend = function (str1_val, str2_val) {
        return this.goAbend(this.objectName() + "." + str1_val, str2_val);
    };

    this.logit = function (str1_val, str2_val) {
        return this.goLogit(this.objectName() + "." + str1_val, str2_val);
    };

    this.goLog = function (s1_val, s2_val) {
        if (this.clusterObject().sessionArrayLength() === 1) {
            this.utilModule().utilLogit(this.clusterObject().sessionArray(0).sessionId() + "<=>" +
                                        this.clusterObject().sessionArray(0).sessionId() + " " + s1_val, s2_val);
        }
        else {
            this.utilModule().utilLogit(this.clusterObject().sessionArray(0).sessionId() + "<=>" +
                                        this.clusterObject().sessionArray(1).sessionId() + " " + s1_val, s2_val);
        }
    };

    this.goAbend = function (s1_val, s2_val) {
        if (this.topicObject().sessionArrayLength() === 1) {
            this.utilModule().utilAbend(this.topicObject().sessionArray(0).sessionId() + "<=>" +
                                        this.topicObject().sessionArray(0).sessionId() + " " + s1_val, s2_val);
        }
        else {
            this.utilModule().utilAbend(this.topicObject().sessionArray(0).sessionId() + "<=>" +
                                        this.topicObject().sessionArray(1).sessionId() + " " + s1_val, s2_val);
        }
    };

    this.startGoGame = function () {
        this.gameObject().processTheWholeMoveList();
        this.sessionObject().setupClientReceiveCallback(function (container_val, data_val) {
            //console.log("ajaxReceiveCallBack" + port_val.objectName());
            container_val.portObject().receiveStringData(data_val);
        }, this);
    };

    this.runGoGame = function () {
        var this0 = this;
        this.startGoGame();
        this.rootObject().htmlObject().createPlayHolders();
        this.uiObject().initElements();
        this.uiObject().drawBoard(this.engineObject());

        $("canvas").on("click", function(event) {
            this0.uiObject().uiClickApi(event.clientX, event.clientY);
        });

        $("canvas").on("mousemove", function(event) {
            this0.uiObject().uiMouseMove(event.clientX, event.clientY);
        });

        var addCommentFromInputBox = function () {
            var $new_comment = $("<p>");
            if ($(".comment-input input").val() !== "") {
                $new_comment.text($(".comment-input input").val());
                $new_comment.hide();
                $(".comments").append($new_comment);
                $new_comment.fadeIn();
                $(".comment-input input").val("");
            }
        };

        $(".comment-input button").on("click", function(event) {
            addCommentFromInputBox();
        });

        $(".comment-input input").on("keypress", function(event) {
            if (event.keyCode == 13) {
                addCommentFromInputBox();
            }
        });
    };

    this.mallocObjects();
}

var GO = new GoDefineObject;
function GoDefineObject() {
    this.objectName = "GoDefineObject";

    this.THE_FORWARD_MOVE = "FORWARD";
    this.THE_BACKWARD_MOVE = "BACKWARD";
    this.THE_DOUBLE_FORWARD_MOVE = "DOUBLE_FORWARD";
    this.THE_DOUBLE_BACKWARD_MOVE = "DOUBLE_BACKWARD";
    this.THE_PASS_MOVE = "PASS";
    this.THE_RESIGN_MOVE = "RESIGN";
    this.THE_BACK_TO_PLAY_MOVE = "BACK_TO_PLAY";
    this.THE_CONFIRM_MOVE = "CONFIRM";
    this.THE_PLAY_ANOTHER_GAME_MOVE = "PLAY_ANOTHER_GAME";

    this.THE_EMPTY_STONE = 0;
    this.THE_BLACK_STONE = 1;
    this.THE_WHITE_STONE = 2;
    this.THE_BOTH_COLOR_STONE = 2;
    this.THE_MARK_DEAD_STONE_DIFF = 4;
    this.THE_MARK_EMPTY_STONE_DIFF = 6;

    this.THE_MARKED_DEAD_BLACK_STONE  = (this.THE_BLACK_STONE + this.THE_MARK_DEAD_STONE_DIFF);
    this.THE_MARKED_DEAD_WHITE_STONE  = (this.THE_WHITE_STONE + this.THE_MARK_DEAD_STONE_DIFF);
    this.THE_MARKED_EMPTY_BLACK_STONE = (this.THE_BLACK_STONE + this.THE_MARK_EMPTY_STONE_DIFF);
    this.THE_MARKED_EMPTY_WHITE_STONE = (this.THE_WHITE_STONE + this.THE_MARK_EMPTY_STONE_DIFF);

    //this.X_Y_VALUE_FOR_PASS = 99;

    this.FORWARD_MOVE = function () {return this.THE_FORWARD_MOVE;};
    this.BACKWARD_MOVE = function () {return this.THE_BACKWARD_MOVE;};
    this.DOUBLE_FORWARD_MOVE = function () {return this.THE_DOUBLE_FORWARD_MOVE;};
    this.DOUBLE_BACKWARD_MOVE = function () {return this.THE_DOUBLE_BACKWARD_MOVE;};
    this.PASS_MOVE = function () {return this.THE_PASS_MOVE;};
    this.RESIGN_MOVE = function () {return this.THE_RESIGN_MOVE;};
    this.BACK_TO_PLAY_MOVE = function () {return this.THE_BACK_TO_PLAY_MOVE;};
    this.CONFIRM_MOVE = function () {return this.THE_CONFIRM_MOVE;};
    this.PLAY_ANOTHER_GAME_MOVE = function () {return this.THE_PLAY_ANOTHER_GAME_MOVE;};

    this.EMPTY_STONE = function () {return this.THE_EMPTY_STONE;};
    this.BLACK_STONE = function () {return this.THE_BLACK_STONE;};
    this.WHITE_STONE = function () {return this.THE_WHITE_STONE;};
    this.BOTH_COLOR_STONE = function () {return this.THE_BOTH_COLOR_STONE;};
    this.MARK_DEAD_STONE_DIFF = function () {return this.THE_MARK_DEAD_STONE_DIFF;};
    this.MARK_EMPTY_STONE_DIFF = function () {return this.THE_MARK_EMPTY_STONE_DIFF;};

    this.MARKED_DEAD_BLACK_STONE = function () {return this.THE_MARKED_DEAD_BLACK_STONE;};
    this.MARKED_DEAD_WHITE_STONE = function () {return this.THE_MARKED_DEAD_WHITE_STONE;};
    this.MARKED_EMPTY_BLACK_STONE = function () {return this.THE_MARKED_EMPTY_BLACK_STONE;};
    this.MARKED_EMPTY_WHITE_STONE = function () {return this.THE_MARKED_EMPTY_WHITE_STONE;};

//    this.X_Y_VALUE_FOR_PASS_ = function () {return this.X_Y_VALUE_FOR_PASS;}

    this.getOppositeColor = function (color_val) {
        switch (color_val) {
        case GO.BLACK_STONE():
            return GO.WHITE_STONE();

        case GO.WHITE_STONE():
            return GO.BLACK_STONE();

        default:
            this.goAbend("getOppositeColor", "color=" + color_val);
            return GO.EMPTY_STONE();
        }
    };

    this.isNeighborStone = function (x1_val, y1_val, x2_val, y2_val) {
        if (x1_val === x2_val) {
            if ((y1_val + 1 === y2_val) || (y1_val - 1 === y2_val)) {
                return true;
            }
        }
        if (y1_val === y2_val) {
            if ((x1_val + 1 === x2_val) || (x1_val - 1 === x2_val)) {
                return true;
            }
        }
        return false;
    };

    this.isValidCoordinates = function (x_val, y_val, board_size_val) {
        return this.isValidCoordinate(x_val, board_size_val) && this.isValidCoordinate(y_val, board_size_val) ;
    };

    this.isValidCoordinate = function (coordinate_val, board_size_val) {
        return (0 <= coordinate_val) && (coordinate_val < board_size_val);
    };

    this.goLog = function (s1_val, s2_val) {
        window.console.log(s1_val + "() " + s2_val);
    };

    this.goAbend = function (s1_val, s2_val) {
        window.console.log("goAbend: " + s1_val + "() " + s2_val);
        window.alert("Abend: " + s1_val + "() " + s2_val);
        var x = junk;
    };
}
<|endoftext|>


<commit_before>"use strict";

const fs = require('fs');
const {promisify} = require('bluebird');
const {EventBus} = require('../utils/event_bus');
const {File} = require('./file');
const {validateFileSystemDependencies} = require('./dependencies');

class FileSystemCache {
  constructor(fileSystem={}) {
    this.fileSystem = {
      readFile: fileSystem.readFile || promisify(fs.readFile),
      stat: fileSystem.stat || promisify(fs.stat)
    };
    this.files = Object.create(null);
    this.trapsByFile = Object.create(null);

    // Incoming data
    this.fileAdded = new EventBus();
    this.fileChanged = new EventBus();
    this.fileRemoved = new EventBus();

    // Outgoing data
    this.trapTriggered = new EventBus();

    // Handle incoming data
    this.fileAdded.subscribe((path, stat) => this._handleFileAdded(path, stat));
    this.fileChanged.subscribe((path, stat) => this._handleFileChanged(path, stat));
    this.fileRemoved.subscribe(path => this._handleFileRemoved(path));
  }
  isFile(path) {
    return this._evaluateFileMethod('getIsFile', path);
  }
  stat(path) {
    return this._evaluateFileMethod('getStat', path);
  }
  readModifiedTime(path) {
    return this._evaluateFileMethod('getModifiedTime', path);
  }
  readBuffer(path) {
    return this._evaluateFileMethod('getBuffer', path);
  }
  readText(path) {
    return this._evaluateFileMethod('getText', path);
  }
  readTextHash(path) {
    return this._evaluateFileMethod('getTextHash', path);
  }
  createTrap() {
    return new FileSystemTrap(this);
  }
  rehydrateTrap(dependencies) {
    return validateFileSystemDependencies(this, dependencies)
      .then(isValid => {
        if (isValid) {
          const trap = this.createTrap();
          for (var path in dependencies) {
            if (dependencies.hasOwnProperty(path)) {
              trap.bindings[path] = dependencies[path];
              this._bindTrapToFile(trap, path);
            }
          }
          return trap;
        }
        return null;
      });
  }
  _bindTrapToFile(trap, path) {
    let traps = this.trapsByFile[path];
    if (!traps) {
      traps = new Set();
      this.trapsByFile[path] = traps;
    }
    traps.add(trap);
  }
  _handleFileAdded(path, stat) {
    let file = this.files[path];
    if (!file) {
      this._createFile(path, stat);
    }

    const traps = this.trapsByFile[path];
    if (traps && traps.size) {
      const trapsToTrigger = [];
      for (const trap of traps) {
        const bindings = trap.bindings[path];
        if (bindings && bindings.isFile === false) {
          trapsToTrigger.push(trap);
        }
      }
      this._triggerTraps(trapsToTrigger, path, 'added');
    }
  }
  _handleFileChanged(path, stat) {
    this._removeFile(path);
    this._createFile(path, stat);

    const traps = this.trapsByFile[path];
    if (traps && traps.size) {
      const trapsToTrigger = [];
      for (const trap of traps) {
        if (trap.triggerOnChange[path]) {
          trapsToTrigger.push(trap);
        } else {
          const bindings = trap.bindings[path];
          if (bindings && (bindings.modifiedTime !== undefined || bindings.textHash !== undefined)) {
            trapsToTrigger.push(trap);
          }
        }
      }
      this._triggerTraps(trapsToTrigger, path, 'changed');
    }
  }
  _handleFileRemoved(path) {
    this._removeFile(path);
    const file = this._createFile(path);
    // Pre-populate the file's data
    file.setIsFile(false);

    const traps = this.trapsByFile[path];
    if (traps && traps.size) {
      const trapsToTrigger = [];
      for (const trap of traps) {
        if (trap.bindings[path].isFile === true) {
          trapsToTrigger.push(trap);
        }
      }
      this._triggerTraps(trapsToTrigger, path, 'removed');
    }
  }
  _createFile(path, stat) {
    const file = new File(path, this.fileSystem);
    if (stat) {
      // Prepopulate the file's data
      file.setStat(stat);
      file.setIsFile(stat.isFile());
      file.setModifiedTime(stat.mtime.getTime());
    }
    this.files[path] = file;
    return file;
  }
  _removeFile(path) {
    this.files[path] = null;
  }
  _evaluateFileMethod(methodName, path) {
    let file = this.files[path];
    if (!file) {
      file = this._createFile(path);
    }
    return file[methodName]()
      .catch(err => {
        const block = this._blockStaleFilesFromResolving(file);
        return block || Promise.reject(err);
      })
      .then(data => {
        const block = this._blockStaleFilesFromResolving(file);
        return block || data;
      });
  }
  _blockStaleFilesFromResolving(file) {
    // If the file was removed during processing, we prevent the promise
    // chain from continuing and rely on the traps to signal the change
    if (file !== this.files[file.path]) {
      return new Promise(() => {});
    }
    return null;
  }
  _triggerTraps(traps, path, cause) {
    const length = traps.length;
    if (length === 0) {
      return;
    }
    let i = length;
    // To avoid any inexplicable behaviour due to synchronous code evaluation
    // feeding back into the cache, we disengage every trap before signalling
    // any subscribers
    while(--i !== -1) {
      const trap = traps[i];
      for (const path in trap.bindings) {
        this.trapsByFile[path].delete(trap);
      }
    }
    i = length;
    while(--i !== -1) {
      this.trapTriggered.push({
        trap: traps[i],
        path,
        cause
      });
    }
  }
}

class FileSystemTrap {
  constructor(cache) {
    this.cache = cache;
    this.bindings = Object.create(null);
    this.boundFiles = Object.create(null);
    this.triggerOnChange = Object.create(null);
  }
  isFile(path) {
    return this.cache.isFile(path)
      .then(isFile => {
        const bindings = this._getFileBindings(path);
        if (bindings.isFile === undefined) {
          bindings.isFile = isFile;
        }
        return isFile;
      });
  }
  stat(path) {
    this._ensureBindingToFile(path);
    this.triggerOnChange[path] = true;
    return this.cache.stat(path)
      .then(stat => {
        const bindings = this._getFileBindings(path);
        bindings.isFile = true;
        if (bindings.modifiedTime === undefined) {
          bindings.modifiedTime = stat.mtime.getTime();
        }
        return stat;
      });
  }
  readModifiedTime(path) {
    this._ensureBindingToFile(path);
    this.triggerOnChange[path] = true;
    return this.cache.readModifiedTime(path)
      .then(modifiedTime => {
        const bindings = this._getFileBindings(path);
        bindings.isFile = true;
        bindings.modifiedTime = modifiedTime;
        return modifiedTime;
      });
  }
  readBuffer(path) {
    this._ensureBindingToFile(path);
    this.triggerOnChange[path] = true;
    return this.cache.readBuffer(path)
      .then(buffer => {
        // Rely on `readModifiedTime` to bind its dependencies
        return this.readModifiedTime(path)
          .then(() => buffer);
      });
  }
  readText(path) {
    this._ensureBindingToFile(path);
    this.triggerOnChange[path] = true;
    return this.cache.readText(path)
      .then(text => {
        // Rely on `readTextHash` to bind its dependencies
        return this.readTextHash(path)
          .then(() => text);
      });
  }
  readTextHash(path) {
    this._ensureBindingToFile(path);
    this.triggerOnChange[path] = true;
    return this.cache.readTextHash(path)
      .then(textHash => {
        // Rely on `readModifiedTime` to bind its dependencies
        return this.readModifiedTime(path)
          .then(() => {
            const bindings = this._getFileBindings(path);
            if (bindings.textHash === undefined) {
              bindings.textHash = textHash;
            }
            return textHash;
          });
      });
  }
  describeDependencies() {
    return this.bindings;
  }
  _ensureBindingToFile(path) {
    if (this.boundFiles[path] === undefined) {
      this.boundFiles[path] = true;
      this.cache._bindTrapToFile(this, path);
    }
  }
  _getFileBindings(path) {
    let bindings = this.bindings[path];
    if (!bindings) {
      bindings = {};
      this.bindings[path] = bindings;
      this._ensureBindingToFile(path);
    }
    return bindings;
  }
}

module.exports = {
  FileSystemCache,
  FileSystemTrap
};<commit_msg>Lint conformance<commit_after>"use strict";

const fs = require('fs');
const {promisify} = require('bluebird');
const {EventBus} = require('../utils/event_bus');
const {File} = require('./file');
const {validateFileSystemDependencies} = require('./dependencies');

class FileSystemCache {
  constructor(fileSystem={}) {
    this.fileSystem = {
      readFile: fileSystem.readFile || promisify(fs.readFile),
      stat: fileSystem.stat || promisify(fs.stat)
    };
    this.files = Object.create(null);
    this.trapsByFile = Object.create(null);

    // Incoming data
    this.fileAdded = new EventBus();
    this.fileChanged = new EventBus();
    this.fileRemoved = new EventBus();

    // Outgoing data
    this.trapTriggered = new EventBus();

    // Handle incoming data
    this.fileAdded.subscribe((path, stat) => this._handleFileAdded(path, stat));
    this.fileChanged.subscribe((path, stat) => this._handleFileChanged(path, stat));
    this.fileRemoved.subscribe(path => this._handleFileRemoved(path));
  }
  isFile(path) {
    return this._evaluateFileMethod('getIsFile', path);
  }
  stat(path) {
    return this._evaluateFileMethod('getStat', path);
  }
  readModifiedTime(path) {
    return this._evaluateFileMethod('getModifiedTime', path);
  }
  readBuffer(path) {
    return this._evaluateFileMethod('getBuffer', path);
  }
  readText(path) {
    return this._evaluateFileMethod('getText', path);
  }
  readTextHash(path) {
    return this._evaluateFileMethod('getTextHash', path);
  }
  createTrap() {
    return new FileSystemTrap(this);
  }
  rehydrateTrap(dependencies) {
    return validateFileSystemDependencies(this, dependencies)
      .then(isValid => {
        if (isValid) {
          const trap = this.createTrap();
          for (var path in dependencies) {
            if (dependencies.hasOwnProperty(path)) {
              trap.bindings[path] = dependencies[path];
              this._bindTrapToFile(trap, path);
            }
          }
          return trap;
        }
        return null;
      });
  }
  _bindTrapToFile(trap, path) {
    let traps = this.trapsByFile[path];
    if (!traps) {
      traps = new Set();
      this.trapsByFile[path] = traps;
    }
    traps.add(trap);
  }
  _handleFileAdded(path, stat) {
    const file = this.files[path];
    if (!file) {
      this._createFile(path, stat);
    }

    const traps = this.trapsByFile[path];
    if (traps && traps.size) {
      const trapsToTrigger = [];
      for (const trap of traps) {
        const bindings = trap.bindings[path];
        if (bindings && bindings.isFile === false) {
          trapsToTrigger.push(trap);
        }
      }
      this._triggerTraps(trapsToTrigger, path, 'added');
    }
  }
  _handleFileChanged(path, stat) {
    this._removeFile(path);
    this._createFile(path, stat);

    const traps = this.trapsByFile[path];
    if (traps && traps.size) {
      const trapsToTrigger = [];
      for (const trap of traps) {
        if (trap.triggerOnChange[path]) {
          trapsToTrigger.push(trap);
        } else {
          const bindings = trap.bindings[path];
          if (bindings && (bindings.modifiedTime !== undefined || bindings.textHash !== undefined)) {
            trapsToTrigger.push(trap);
          }
        }
      }
      this._triggerTraps(trapsToTrigger, path, 'changed');
    }
  }
  _handleFileRemoved(path) {
    this._removeFile(path);
    const file = this._createFile(path);
    // Pre-populate the file's data
    file.setIsFile(false);

    const traps = this.trapsByFile[path];
    if (traps && traps.size) {
      const trapsToTrigger = [];
      for (const trap of traps) {
        if (trap.bindings[path].isFile === true) {
          trapsToTrigger.push(trap);
        }
      }
      this._triggerTraps(trapsToTrigger, path, 'removed');
    }
  }
  _createFile(path, stat) {
    const file = new File(path, this.fileSystem);
    if (stat) {
      // Prepopulate the file's data
      file.setStat(stat);
      file.setIsFile(stat.isFile());
      file.setModifiedTime(stat.mtime.getTime());
    }
    this.files[path] = file;
    return file;
  }
  _removeFile(path) {
    this.files[path] = null;
  }
  _evaluateFileMethod(methodName, path) {
    let file = this.files[path];
    if (!file) {
      file = this._createFile(path);
    }
    return file[methodName]()
      .catch(err => {
        const block = this._blockStaleFilesFromResolving(file);
        return block || Promise.reject(err);
      })
      .then(data => {
        const block = this._blockStaleFilesFromResolving(file);
        return block || data;
      });
  }
  _blockStaleFilesFromResolving(file) {
    // If the file was removed during processing, we prevent the promise
    // chain from continuing and rely on the traps to signal the change
    if (file !== this.files[file.path]) {
      return new Promise(() => {});
    }
    return null;
  }
  _triggerTraps(traps, path, cause) {
    const length = traps.length;
    if (length === 0) {
      return;
    }
    let i = length;
    // To avoid any inexplicable behaviour due to synchronous code evaluation
    // feeding back into the cache, we disengage every trap before signalling
    // any subscribers
    while(--i !== -1) {
      const trap = traps[i];
      for (const path in trap.bindings) {
        this.trapsByFile[path].delete(trap);
      }
    }
    i = length;
    while(--i !== -1) {
      this.trapTriggered.push({
        trap: traps[i],
        path,
        cause
      });
    }
  }
}

class FileSystemTrap {
  constructor(cache) {
    this.cache = cache;
    this.bindings = Object.create(null);
    this.boundFiles = Object.create(null);
    this.triggerOnChange = Object.create(null);
  }
  isFile(path) {
    return this.cache.isFile(path)
      .then(isFile => {
        const bindings = this._getFileBindings(path);
        if (bindings.isFile === undefined) {
          bindings.isFile = isFile;
        }
        return isFile;
      });
  }
  stat(path) {
    this._ensureBindingToFile(path);
    this.triggerOnChange[path] = true;
    return this.cache.stat(path)
      .then(stat => {
        const bindings = this._getFileBindings(path);
        bindings.isFile = true;
        if (bindings.modifiedTime === undefined) {
          bindings.modifiedTime = stat.mtime.getTime();
        }
        return stat;
      });
  }
  readModifiedTime(path) {
    this._ensureBindingToFile(path);
    this.triggerOnChange[path] = true;
    return this.cache.readModifiedTime(path)
      .then(modifiedTime => {
        const bindings = this._getFileBindings(path);
        bindings.isFile = true;
        bindings.modifiedTime = modifiedTime;
        return modifiedTime;
      });
  }
  readBuffer(path) {
    this._ensureBindingToFile(path);
    this.triggerOnChange[path] = true;
    return this.cache.readBuffer(path)
      .then(buffer => {
        // Rely on `readModifiedTime` to bind its dependencies
        return this.readModifiedTime(path)
          .then(() => buffer);
      });
  }
  readText(path) {
    this._ensureBindingToFile(path);
    this.triggerOnChange[path] = true;
    return this.cache.readText(path)
      .then(text => {
        // Rely on `readTextHash` to bind its dependencies
        return this.readTextHash(path)
          .then(() => text);
      });
  }
  readTextHash(path) {
    this._ensureBindingToFile(path);
    this.triggerOnChange[path] = true;
    return this.cache.readTextHash(path)
      .then(textHash => {
        // Rely on `readModifiedTime` to bind its dependencies
        return this.readModifiedTime(path)
          .then(() => {
            const bindings = this._getFileBindings(path);
            if (bindings.textHash === undefined) {
              bindings.textHash = textHash;
            }
            return textHash;
          });
      });
  }
  describeDependencies() {
    return this.bindings;
  }
  _ensureBindingToFile(path) {
    if (this.boundFiles[path] === undefined) {
      this.boundFiles[path] = true;
      this.cache._bindTrapToFile(this, path);
    }
  }
  _getFileBindings(path) {
    let bindings = this.bindings[path];
    if (!bindings) {
      bindings = {};
      this.bindings[path] = bindings;
      this._ensureBindingToFile(path);
    }
    return bindings;
  }
}

module.exports = {
  FileSystemCache,
  FileSystemTrap
};<|endoftext|>


<commit_before>'use strict';

var Vec3 = require('famous-math/src/Vec3');
var Geometry = require('../../Geometry');
var ObjectManager = require('famous-utilities/src/ObjectManager');

ObjectManager.register('GJK_EPASupportPoint', GJK_EPASupportPoint);
var OMRequestGJK_EPASupportPoint = ObjectManager.requestGJK_EPASupportPoint;
var OMRequestDynamicGeometry = ObjectManager.requestDynamicGeometry;
var OMFreeGJK_EPASupportPoint = ObjectManager.freeGJK_EPASupportPoint;
var OMFreeDynamicGeometry = ObjectManager.freeDynamicGeometry;
var OMFreeDynamicGeometryFeature = ObjectManager.freeDynamicGeometryFeature;

var P_REGISTER = new Vec3();
var V0_REGISTER = new Vec3();
var V1_REGISTER = new Vec3();
var V2_REGISTER = new Vec3();

var DIRECTION_REGISTER = new Vec3();
var INVDIRECTION_REGISTER = new Vec3();

/**
 * Support point to be added to the DynamicGeometry. The point in Minkowski space as well as the
 * original pair.
 *
 * @class GJK_EPASupportPoint
 * @param {Vec3} vertex
 * @param {Vec3} worldVertexA
 * @param {Vec3} worldVertexAB
 */
function GJK_EPASupportPoint(vertex, worldVertexA, worldVertexB) {
    this.vertex = vertex;
    this.worldVertexA = worldVertexA;
    this.worldVertexB = worldVertexB;
}

/**
 * Used by ObjectManager to reset the object with different data.
 *
 * @method reset
 * @param {Object[]} args
 * @chainable
 */
GJK_EPASupportPoint.prototype.reset = function reset(vertex, worldVertexA, worldVertexB) {
    this.vertex = vertex;
    this.worldVertexA = worldVertexA;
    this.worldVertexB = worldVertexB;

    return this;
};

/**
 * Free the DynamicGeomtetry and associate vertices and features for later reuse.
 *
 * @method freeGJK_EPADynamicGeometry
 * @param {DynamicGeometry} geometry
 */
function freeGJK_EPADynamicGeometry(geometry) {
    var vertices = geometry.vertices;
    var i = vertices.length;
    while (i--) {
        var v = vertices[i];
        if (v !== null) OMFreeGJK_EPASupportPoint(v);
    }
    geometry.vertices = [];
    var features = geometry.features;
    var i = features.length
    while (i--) {
        var f = features[i];
        if (f !== null) OMFreeDynamicGeometryFeature(f);
    }
    geometry.features = [];
    OMFreeDynamicGeometry(geometry);
}

/**
 * Find the point in Minkowski space furthest in a given direction for two convex Bodies.
 *
 * @method minkowskiSupport
 * @param {Body} body1
 * @param {Body} body2
 * @param {Vec3} direction
 * @return {GJK_EPASupportPoint}
 */
function minkowskiSupport(body1, body2, direction) {
    var inverseDirection = Vec3.scale(direction, -1, INVDIRECTION_REGISTER);

    var furthest1 = body1.support(direction);
    var furthest2 = body2.support(inverseDirection);

    var w1 = Vec3.add(furthest1, body1.position, new Vec3());
    var w2 = Vec3.add(furthest2, body2.position, new Vec3());

    // The vertex in Minkowski space as well as the original pair in world space
    return OMRequestGJK_EPASupportPoint().reset(Vec3.subtract(w1, w2, new Vec3()), w1, w2);
}

/**
 * Gilbert-Johnson-Keerthi collision detection. Returns a DynamicGeometry simplex if the bodies are found
 * to have collided or false for no collsion.
 *
 * @method GJK
 * param {Body} body1
 * param {Body} body2
 * @return {DynamicGeometry | Boolean}
 */
function GJK(body1, body2) {
    var support = minkowskiSupport;
    // Use p2 - p1 to seed the initial choice of direction
    var direction = Vec3.subtract(body2.position, body1.position, DIRECTION_REGISTER).normalize();
    var simplex = OMRequestDynamicGeometry();
    simplex.addVertex(support(body1, body2, direction));
    direction.invert();

    var i = 0;
    var maxIterations = 1e3;
    while(i++ < maxIterations) {
        if (direction.x === 0 && direction.y === 0 && direction.z === 0) break;
        simplex.addVertex(support(body1, body2, direction));
        if (Vec3.dot(simplex.getLastVertex().vertex, direction) < 0) break;
        // If simplex contains origin, return for use in EPA
        if (simplex.simplexContainsOrigin(direction, OMFreeGJK_EPASupportPoint)) return simplex;
    }
    freeGJK_EPADynamicGeometry(simplex);
    return false;
}

/**
 * Expanding Polytope Algorithm--penetration depth, collision normal, and contact points.
 * Returns a CollisonData object.
 *
 * @method EPA
 * @param {Body} body1
 * @param {Body} body2
 * @param {DynamicGeometry} polytope
 * @return {CollisionData}
 */
function EPA(body1, body2, polytope) {
    var support = minkowskiSupport;
    var depthEstimate = Infinity;

    var i = 0;
    var maxIterations = 1e3;
    while(i++ < maxIterations) {
        var closest = polytope.getFeatureClosestToOrigin();
        if (closest === null) return null;
        var direction = closest.normal;
        var point = support(body1, body2, direction);
        depthEstimate = Math.min(depthEstimate, Vec3.dot(point.vertex, direction));
        if (depthEstimate - closest.distance <= 0.01) {
            var supportA = polytope.vertices[closest.vertexIndices[0]];
            var supportB = polytope.vertices[closest.vertexIndices[1]];
            var supportC = polytope.vertices[closest.vertexIndices[2]];

            var A = supportA.vertex;
            var B = supportB.vertex;
            var C = supportC.vertex;
            var P = Vec3.scale(direction, closest.distance, P_REGISTER);

            var V0 = Vec3.subtract(B, A, V0_REGISTER);
            var V1 = Vec3.subtract(C, A, V1_REGISTER);
            var V2 = Vec3.subtract(P, A, V2_REGISTER);

            var d00 = Vec3.dot(V0, V0);
            var d01 = Vec3.dot(V0, V1);
            var d11 = Vec3.dot(V1, V1);
            var d20 = Vec3.dot(V2, V0);
            var d21 = Vec3.dot(V2, V1);
            var denom = d00*d11 - d01*d01;

            var v = (d11*d20 - d01*d21) / denom;
            var w = (d00*d21 - d01*d20) / denom;
            var u = 1.0 - v - w;

            var body1Contact =      supportA.worldVertexA.scale(u)
                               .add(supportB.worldVertexA.scale(v))
                               .add(supportC.worldVertexA.scale(w));

            var body2Contact =      supportA.worldVertexB.scale(u)
                               .add(supportB.worldVertexB.scale(v))
                               .add(supportC.worldVertexB.scale(w));

            var localBody1Contact = Vec3.subtract(body1Contact, body1.position, new Vec3());
            var localBody2Contact = Vec3.subtract(body2Contact, body2.position, new Vec3());

            freeGJK_EPADynamicGeometry(polytope);
            OMFreeGJK_EPASupportPoint(point);

            return ObjectManager.requestCollisionData().reset(closest.distance, direction, body1Contact, body2Contact, localBody1Contact, localBody2Contact);
        } else {
            polytope.addVertex(point);
            polytope.reshape();
        }
    }
    throw new Error('EPA failed to terminate in allotted iterations.');
}

module.exports.GJK = GJK;
module.exports.EPA = EPA;
<commit_msg>fix: reuse vertices and features arrays<commit_after>'use strict';

var Vec3 = require('famous-math/src/Vec3');
var Geometry = require('../../Geometry');
var ObjectManager = require('famous-utilities/src/ObjectManager');

ObjectManager.register('GJK_EPASupportPoint', GJK_EPASupportPoint);
var OMRequestGJK_EPASupportPoint = ObjectManager.requestGJK_EPASupportPoint;
var OMRequestDynamicGeometry = ObjectManager.requestDynamicGeometry;
var OMFreeGJK_EPASupportPoint = ObjectManager.freeGJK_EPASupportPoint;
var OMFreeDynamicGeometry = ObjectManager.freeDynamicGeometry;
var OMFreeDynamicGeometryFeature = ObjectManager.freeDynamicGeometryFeature;

var P_REGISTER = new Vec3();
var V0_REGISTER = new Vec3();
var V1_REGISTER = new Vec3();
var V2_REGISTER = new Vec3();

var DIRECTION_REGISTER = new Vec3();
var INVDIRECTION_REGISTER = new Vec3();

/**
 * Support point to be added to the DynamicGeometry. The point in Minkowski space as well as the
 * original pair.
 *
 * @class GJK_EPASupportPoint
 * @param {Vec3} vertex
 * @param {Vec3} worldVertexA
 * @param {Vec3} worldVertexAB
 */
function GJK_EPASupportPoint(vertex, worldVertexA, worldVertexB) {
    this.vertex = vertex;
    this.worldVertexA = worldVertexA;
    this.worldVertexB = worldVertexB;
}

/**
 * Used by ObjectManager to reset the object with different data.
 *
 * @method reset
 * @param {Object[]} args
 * @chainable
 */
GJK_EPASupportPoint.prototype.reset = function reset(vertex, worldVertexA, worldVertexB) {
    this.vertex = vertex;
    this.worldVertexA = worldVertexA;
    this.worldVertexB = worldVertexB;

    return this;
};

/**
 * Free the DynamicGeomtetry and associate vertices and features for later reuse.
 *
 * @method freeGJK_EPADynamicGeometry
 * @param {DynamicGeometry} geometry
 */
function freeGJK_EPADynamicGeometry(geometry) {
    var vertices = geometry.vertices;
    var i = vertices.length;
    while (i--) {
        var v = vertices.pop();
        if (v !== null) OMFreeGJK_EPASupportPoint(v);
    }
    geometry.numVertices = 0;
    var features = geometry.features;
    var i = features.length
    while (i--) {
        var f = features.pop();
        if (f !== null) OMFreeDynamicGeometryFeature(f);
    }
    geometry.numFeatures = 0;
    OMFreeDynamicGeometry(geometry);
}

/**
 * Find the point in Minkowski space furthest in a given direction for two convex Bodies.
 *
 * @method minkowskiSupport
 * @param {Body} body1
 * @param {Body} body2
 * @param {Vec3} direction
 * @return {GJK_EPASupportPoint}
 */
function minkowskiSupport(body1, body2, direction) {
    var inverseDirection = Vec3.scale(direction, -1, INVDIRECTION_REGISTER);

    var furthest1 = body1.support(direction);
    var furthest2 = body2.support(inverseDirection);

    var w1 = Vec3.add(furthest1, body1.position, new Vec3());
    var w2 = Vec3.add(furthest2, body2.position, new Vec3());

    // The vertex in Minkowski space as well as the original pair in world space
    return OMRequestGJK_EPASupportPoint().reset(Vec3.subtract(w1, w2, new Vec3()), w1, w2);
}

/**
 * Gilbert-Johnson-Keerthi collision detection. Returns a DynamicGeometry simplex if the bodies are found
 * to have collided or false for no collsion.
 *
 * @method GJK
 * param {Body} body1
 * param {Body} body2
 * @return {DynamicGeometry | Boolean}
 */
function GJK(body1, body2) {
    var support = minkowskiSupport;
    // Use p2 - p1 to seed the initial choice of direction
    var direction = Vec3.subtract(body2.position, body1.position, DIRECTION_REGISTER).normalize();
    var simplex = OMRequestDynamicGeometry();
    simplex.addVertex(support(body1, body2, direction));
    direction.invert();

    var i = 0;
    var maxIterations = 1e3;
    while(i++ < maxIterations) {
        if (direction.x === 0 && direction.y === 0 && direction.z === 0) break;
        simplex.addVertex(support(body1, body2, direction));
        if (Vec3.dot(simplex.getLastVertex().vertex, direction) < 0) break;
        // If simplex contains origin, return for use in EPA
        if (simplex.simplexContainsOrigin(direction, OMFreeGJK_EPASupportPoint)) return simplex;
    }
    freeGJK_EPADynamicGeometry(simplex);
    return false;
}

/**
 * Expanding Polytope Algorithm--penetration depth, collision normal, and contact points.
 * Returns a CollisonData object.
 *
 * @method EPA
 * @param {Body} body1
 * @param {Body} body2
 * @param {DynamicGeometry} polytope
 * @return {CollisionData}
 */
function EPA(body1, body2, polytope) {
    var support = minkowskiSupport;
    var depthEstimate = Infinity;

    var i = 0;
    var maxIterations = 1e3;
    while(i++ < maxIterations) {
        var closest = polytope.getFeatureClosestToOrigin();
        if (closest === null) return null;
        var direction = closest.normal;
        var point = support(body1, body2, direction);
        depthEstimate = Math.min(depthEstimate, Vec3.dot(point.vertex, direction));
        if (depthEstimate - closest.distance <= 0.01) {
            var supportA = polytope.vertices[closest.vertexIndices[0]];
            var supportB = polytope.vertices[closest.vertexIndices[1]];
            var supportC = polytope.vertices[closest.vertexIndices[2]];

            var A = supportA.vertex;
            var B = supportB.vertex;
            var C = supportC.vertex;
            var P = Vec3.scale(direction, closest.distance, P_REGISTER);

            var V0 = Vec3.subtract(B, A, V0_REGISTER);
            var V1 = Vec3.subtract(C, A, V1_REGISTER);
            var V2 = Vec3.subtract(P, A, V2_REGISTER);

            var d00 = Vec3.dot(V0, V0);
            var d01 = Vec3.dot(V0, V1);
            var d11 = Vec3.dot(V1, V1);
            var d20 = Vec3.dot(V2, V0);
            var d21 = Vec3.dot(V2, V1);
            var denom = d00*d11 - d01*d01;

            var v = (d11*d20 - d01*d21) / denom;
            var w = (d00*d21 - d01*d20) / denom;
            var u = 1.0 - v - w;

            var body1Contact =      supportA.worldVertexA.scale(u)
                               .add(supportB.worldVertexA.scale(v))
                               .add(supportC.worldVertexA.scale(w));

            var body2Contact =      supportA.worldVertexB.scale(u)
                               .add(supportB.worldVertexB.scale(v))
                               .add(supportC.worldVertexB.scale(w));

            var localBody1Contact = Vec3.subtract(body1Contact, body1.position, new Vec3());
            var localBody2Contact = Vec3.subtract(body2Contact, body2.position, new Vec3());

            freeGJK_EPADynamicGeometry(polytope);
            OMFreeGJK_EPASupportPoint(point);

            return ObjectManager.requestCollisionData().reset(closest.distance, direction, body1Contact, body2Contact, localBody1Contact, localBody2Contact);
        } else {
            polytope.addVertex(point);
            polytope.reshape();
        }
    }
    throw new Error('EPA failed to terminate in allotted iterations.');
}

module.exports.GJK = GJK;
module.exports.EPA = EPA;
<|endoftext|>