# Copyright 2019 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Tests for module_ir."""

from __future__ import print_function

import collections
import pkgutil
import unittest

from compiler.front_end import module_ir
from compiler.front_end import parser
from compiler.front_end import tokenizer
from compiler.util import ir_data
from compiler.util import ir_data_fields
from compiler.util import ir_data_utils
from compiler.util import test_util

_TESTDATA_PATH = "testdata.golden"
_MINIMAL_SOURCE = pkgutil.get_data(
        _TESTDATA_PATH, "span_se_log_file_status.emb").decode(encoding="UTF-8")
_MINIMAL_SAMPLE = parser.parse_module(
    tokenizer.tokenize(_MINIMAL_SOURCE, "")[0]).parse_tree
_MINIMAL_SAMPLE_IR = ir_data_utils.IrDataSerializer.from_json(ir_data.Module,
    pkgutil.get_data(_TESTDATA_PATH, "span_se_log_file_status.ir.txt").decode(
        encoding="UTF-8")
)

# _TEST_CASES contains test cases, separated by '===', that ensure that specific
# results show up in the IR for .embs.
#
# Each test case is of the form:
#
#     name
#     ---
#     .emb text
#     ---
#     (incomplete) IR text format
#
# For each test case, the .emb is parsed into a parse tree, which is fed into
# module_ir.build_ir(), which should successfully return an IR.  The generated
# IR is then compared against the incomplete IR in the test case to ensure that
# the generated IR is a strict superset of the test case IR -- that is, it is OK
# if the generated IR contains fields that are not in the test case, but not if
# the test case contains fields that are not in the generated IR, and not if the
# test case contains fields whose values differ from the generated IR.
#
# Additionally, for each test case, a pass is executed to ensure that the source
# code location for each node in the IR is strictly contained within the source
# location for its parent node.
_TEST_CASES = r"""
prelude
---
external UInt:
  [fixed_size: false]
  [byte_order_dependent: true]

external Byte:
  [size: 1]
  [byte_order_dependent: false]
---
{
  "type": [
    {
      "external": {},
      "name": { "name": { "text": "UInt" } },
      "attribute": [
        {
          "name": { "text": "fixed_size" },
          "value": { "expression": { "boolean_constant": { "value": false } } }
        },
        {
          "name": { "text": "byte_order_dependent" },
          "value": { "expression": { "boolean_constant": { "value": true } } }
        }
      ]
    },
    {
      "external": {},
      "name": { "name": { "text": "Byte" } },
      "attribute": [
        {
          "name": { "text": "size" },
          "value": { "expression": { "constant": { "value": "1" } } }
        },
        {
          "name": { "text": "byte_order_dependent" },
          "value": { "expression": { "boolean_constant": { "value": false } } }
        }
      ]
    }
  ]
}

===
numbers
---
bits Foo:
  0000000000          [+0_000_000_003]   UInt  decimal
  0b00000100          [+0b0000_0111]     UInt  binary
  0b00000000_00001000 [+0b0_00001011]    UInt  binary2
  0b_0_00001100       [+0b_00001111]     UInt  binary3
  0x00000010          [+0x0000_0013]     UInt  hex
  0x00000000_00000014 [+0x0_00000017]    UInt  hex2
  0x_0_00000018       [+0x_0000001b]     UInt  hex3
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": { "name": { "text": "decimal" } },
            "location": {
              "start": { "constant": { "value": "0" } },
              "size": { "constant": { "value": "3" } }
            }
          },
          {
            "name": { "name": { "text": "binary" } },
            "location": {
              "start": { "constant": { "value": "4" } },
              "size": { "constant": { "value": "7" }
              }
            }
          },
          {
            "name": { "name": { "text": "binary2" } },
            "location": {
              "start": { "constant": { "value": "8" } },
              "size": { "constant": { "value": "11" } }
            }
          },
          {
            "name": { "name": { "text": "binary3" } },
            "location": {
              "start": { "constant": { "value": "12" } },
              "size": { "constant": { "value": "15" } }
            }
          },
          {
            "name": { "name": { "text": "hex" } },
            "location": {
              "start": { "constant": { "value": "16" } },
              "size": { "constant": { "value": "19" } }
            }
          },
          {
            "name": { "name": { "text": "hex2" } },
            "location": {
              "start": { "constant": { "value": "20" } },
              "size": { "constant": { "value": "23" } }
            }
          },
          {
            "name": { "name": { "text": "hex3" } },
            "location": {
              "start": { "constant": { "value": "24" } },
              "size": { "constant": { "value": "27" } }
            }
          }
        ]
      }
    }
  ]
}

===
enum
---
enum Kind:
  WIDGET = 0
  SPROCKET = 1
  GEEGAW = 2  # Comment.
  MAX32 = 4294967295
  MAX64 = 9223372036854775807
---
{
  "type": [
    {
      "enumeration": {
        "value": [
          {
            "name": { "name": { "text": "WIDGET" } },
            "value": { "constant": { "value": "0" } }
          },
          {
            "name": { "name": { "text": "SPROCKET" } },
            "value": { "constant": { "value": "1" } }
          },
          {
            "name": { "name": { "text": "GEEGAW" } },
            "value": { "constant": { "value": "2" } }
          },
          {
            "name": { "name": { "text": "MAX32" } },
            "value": { "constant": { "value": "4294967295" } }
          },
          {
            "name": { "name": { "text": "MAX64" } },
            "value": { "constant": { "value": "9223372036854775807" } }
          }
        ]
      },
      "name": { "name": { "text": "Kind" } }
    }
  ]
}

===
struct attribute
---
struct Foo:
  [size: 10]
  0 [+0]  UInt  field
---
{
  "type": [
    {
      "structure": {
        "field": [ { "name": { "name": { "text": "field" } } } ]
      },
      "name": { "name": { "text": "Foo" } },
      "attribute": [
        {
          "name": { "text": "size" },
          "value": { "expression": { "constant": { "value": "10" } } },
          "is_default": false
        }
      ]
    }
  ]
}

===
$default attribute
---
[$default byte_order: "LittleEndian"]
---
{
  "attribute": [
    {
      "name": { "text": "byte_order" },
      "value": { "string_constant": { "text": "LittleEndian" } },
      "is_default": true
    }
  ]
}

===
abbreviations
---
struct Foo:
  0 [+1]  UInt  size (s)
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": { "name": { "text": "size" } },
            "abbreviation": { "text": "s" }
          }
        ]
      }
    }
  ]
}

===
expressions
---
struct Foo:
  0+1 [+2*3]             UInt  plus_times
  4-5 [+(6)]             UInt  minus_paren
  nn [+7*(8+9)]          UInt  name_complex
  10+11+12 [+13*14*15]   UInt  associativity
  16+17*18 [+19*20-21]   UInt  precedence
  -(+1) [+0-(-10)]       UInt  unary_plus_minus
  1 + + 2 [+3 - -4 - 5]  UInt  unary_plus_minus_2
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": { "name": { "text": "plus_times" } },
            "location": {
              "start": {
                "function": {
                  "function": "ADDITION",
                  "function_name": { "text": "+" },
                  "args": [
                    { "constant": { "value": "0" } },
                    { "constant": { "value": "1" } }
                  ]
                }
              },
              "size": {
                "function": {
                  "function": "MULTIPLICATION",
                  "function_name": { "text": "*" },
                  "args": [
                    { "constant": { "value": "2" } },
                    { "constant": { "value": "3" } }
                  ]
                }
              }
            }
          },
          {
            "name": { "name": { "text": "minus_paren" } },
            "location": {
              "start": {
                "function": {
                  "function": "SUBTRACTION",
                  "args": [
                    { "constant": { "value": "4" } },
                    { "constant": { "value": "5" } }
                  ]
                }
              },
              "size": { "constant": { "value": "6" } }
            }
          },
          {
            "name": { "name": { "text": "name_complex" } },
            "location": {
              "start": {
                "field_reference": {
                  "path": [ { "source_name": [ { "text": "nn" } ] } ]
                }
              },
              "size": {
                "function": {
                  "function": "MULTIPLICATION",
                  "args": [
                    { "constant": { "value": "7" } },
                    {
                      "function": {
                        "function": "ADDITION",
                        "args": [
                          { "constant": { "value": "8" } },
                          { "constant": { "value": "9" } }
                        ]
                      }
                    }
                  ]
                }
              }
            }
          },
          {
            "name": { "name": { "text": "associativity" } },
            "location": {
              "start": {
                "function": {
                  "function": "ADDITION",
                  "args": [
                    {
                      "function": {
                        "function": "ADDITION",
                        "args": [
                          { "constant": { "value": "10" } },
                          { "constant": { "value": "11" } }
                        ]
                      }
                    },
                    { "constant": { "value": "12" } }
                  ]
                }
              },
              "size": {
                "function": {
                  "function": "MULTIPLICATION",
                  "args": [
                    {
                      "function": {
                        "function": "MULTIPLICATION",
                        "args": [
                          { "constant": { "value": "13" } },
                          { "constant": { "value": "14" } }
                        ]
                      }
                    },
                    { "constant": { "value": "15" } }
                  ]
                }
              }
            }
          },
          {
            "name": { "name": { "text": "precedence" } },
            "location": {
              "start": {
                "function": {
                  "function": "ADDITION",
                  "args": [
                    { "constant": { "value": "16" } },
                    {
                      "function": {
                        "function": "MULTIPLICATION",
                        "args": [
                          { "constant": { "value": "17" } },
                          { "constant": { "value": "18" } }
                        ]
                      }
                    }
                  ]
                }
              },
              "size": {
                "function": {
                  "function": "SUBTRACTION",
                  "args": [
                    {
                      "function": {
                        "function": "MULTIPLICATION",
                        "args": [
                          { "constant": { "value": "19" } },
                          { "constant": { "value": "20" } }
                        ]
                      }
                    },
                    { "constant": { "value": "21" } }
                  ]
                }
              }
            }
          },
          {
            "name": { "name": { "text": "unary_plus_minus" } },
            "location": {
              "start": {
                "function": {
                  "function": "SUBTRACTION",
                  "function_name": {
                    "text": "-",
                    "source_location": {
                      "start": { "line": 8, "column": 3 },
                      "end": { "line": 8, "column": 4 }
                    }
                  },
                  "args": [
                    {
                      "constant": {
                        "value": "0",
                        "source_location": {
                          "start": { "line": 8, "column": 3 },
                          "end": { "line": 8, "column": 3 }
                        }
                      },
                      "source_location": {
                        "start": { "line": 8, "column": 3 },
                        "end": { "line": 8, "column": 3 }
                      }
                    },
                    {
                      "function": {
                        "function": "ADDITION",
                        "function_name": {
                          "text": "+",
                          "source_location": {
                            "start": { "line": 8, "column": 5 },
                            "end": { "line": 8, "column": 6 }
                          }
                        },
                        "args": [
                          {
                            "constant": { "value": "0" },
                            "source_location": {
                              "start": { "line": 8, "column": 5 },
                              "end": { "line": 8, "column": 5 }
                            }
                          },
                          {
                            "constant": { "value": "1" },
                            "source_location": {
                              "start": { "line": 8, "column": 6 },
                              "end": { "line": 8, "column": 7 }
                            }
                          }
                        ]
                      },
                      "source_location": {
                        "start": { "line": 8, "column": 4 },
                        "end": { "line": 8, "column": 8 }
                      }
                    }
                  ]
                }
              },
              "size": {
                "function": {
                  "function": "SUBTRACTION",
                  "function_name": {
                    "text": "-",
                    "source_location": {
                      "start": { "line": 8, "column": 12 },
                      "end": { "line": 8, "column": 13 }
                    }
                  },
                  "args": [
                    {
                      "constant": {
                        "value": "0",
                        "source_location": {
                          "start": { "line": 8, "column": 11 },
                          "end": { "line": 8, "column": 12 }
                        }
                      },
                      "source_location": {
                        "start": { "line": 8, "column": 11 },
                        "end": { "line": 8, "column": 12 }
                      }
                    },
                    {
                      "function": {
                        "function": "SUBTRACTION",
                        "function_name": {
                          "text": "-",
                          "source_location": {
                            "start": { "line": 8, "column": 14 },
                            "end": { "line": 8, "column": 15 }
                          }
                        },
                        "args": [
                          {
                            "constant": { "value": "0" },
                            "source_location": {
                              "start": { "line": 8, "column": 14 },
                              "end": { "line": 8, "column": 14 }
                            }
                          },
                          {
                            "constant": { "value": "10" },
                            "source_location": {
                              "start": { "line": 8, "column": 15 },
                              "end": { "line": 8, "column": 17 }
                            }
                          }
                        ]
                      },
                      "source_location": {
                        "start": { "line": 8, "column": 13 },
                        "end": { "line": 8, "column": 18 }
                      }
                    }
                  ]
                }
              }
            }
          },
          {
            "name": { "name": { "text": "unary_plus_minus_2" } },
            "location": {
              "start": {
                "function": {
                  "function": "ADDITION",
                  "args": [
                    { "constant": { "value": "1" } },
                    {
                      "function": {
                        "function": "ADDITION",
                        "args": [
                          { "constant": { "value": "0" } },
                          { "constant": { "value": "2" } }
                        ]
                      }
                    }
                  ]
                }
              },
              "size": {
                "function": {
                  "function": "SUBTRACTION",
                  "args": [
                    {
                      "function": {
                        "function": "SUBTRACTION",
                        "args": [
                          { "constant": { "value": "3" } },
                          {
                            "function": {
                              "function": "SUBTRACTION",
                              "args": [
                                { "constant": { "value": "0" } },
                                { "constant": { "value": "4" } }
                              ]
                            }
                          }
                        ]
                      }
                    },
                    { "constant": { "value": "5" } }
                  ]
                }
              }
            }
          }
        ]
      }
    }
  ]
}

===
auto array size
---
struct TenElementArray:
  0 [+10]  Byte[]  bytes
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "type": {
              "array_type": {
                "base_type": {
                  "atomic_type": {
                    "reference": { "source_name": [ { "text": "Byte" } ] }
                  }
                },
                "automatic": {
                  "source_location": {
                    "start": { "line": 3, "column": 16 },
                    "end": { "line": 3, "column": 18 }
                  }
                }
              }
            },
            "name": { "name": { "text": "bytes" } }
          }
        ]
      }
    }
  ]
}

===
start [+size] ranges
---
struct Foo:
  0 [ + 1 ]  UInt     zero_plus_one
  s [+2]     UInt     s_plus_two
  s [+t]     Byte[t]  s_plus_t
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": { "name": { "text": "zero_plus_one" } },
            "location": {
              "start": {
                "constant": { "value": "0" },
                "source_location": {
                  "start": { "line": 3, "column": 3 },
                  "end": { "line": 3, "column": 4 }
                }
              },
              "size": {
                "constant": { "value": "1" },
                "source_location": {
                  "start": { "line": 3, "column": 9 },
                  "end": { "line": 3, "column": 10 }
                }
              }
            }
          },
          {
            "name": { "name": { "text": "s_plus_two" } },
            "location": {
              "start": {
                "field_reference": {
                  "path": [ { "source_name": [ { "text": "s" } ] } ]
                }
              },
              "size": { "constant": { "value": "2" } }
            }
          },
          {
            "name": { "name": { "text": "s_plus_t" } },
            "location": {
              "start": {
                "field_reference": {
                  "path": [ { "source_name": [ { "text": "s" } ] } ]
                }
              },
              "size": {
                "field_reference": {
                  "path": [ { "source_name": [ { "text": "t" } ] } ]
                }
              }
            }
          }
        ]
      }
    }
  ]
}

===
Using Enum.VALUEs in expressions
---
struct Foo:
  0 [+0+Number.FOUR]  UInt               length_four
  Number.FOUR [+8]    UInt               start_four
  8 [+3*Number.FOUR]  UInt               end_four
  12 [+16]            Byte[Number.FOUR]  array_size_four

enum Number:
  FOUR = 4
  EIGHT = FOUR + Number.FOUR
  SIXTEEN = Number.FOUR * FOUR
  INVALID = Number.NaN.FOUR
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": { "name": { "text": "length_four" } },
            "location": {
              "size": {
                "function": {
                  "function": "ADDITION",
                  "args": [
                    { "constant": { "value": "0" } },
                    {
                      "constant_reference": {
                        "source_name": [
                          { "text": "Number" },
                          { "text": "FOUR" }
                        ]
                      }
                    }
                  ]
                }
              }
            }
          },
          {
            "name": { "name": { "text": "start_four" } },
            "location": {
              "start": {
                "constant_reference": {
                  "source_name": [
                    { "text": "Number" },
                    { "text": "FOUR" }
                  ]
                }
              }
            }
          },
          {
            "name": { "name": { "text": "end_four" } },
            "location": {
              "size": {
                "function": {
                  "function": "MULTIPLICATION",
                  "args": [
                    { "constant": { "value": "3" } },
                    {
                      "constant_reference": {
                        "source_name": [
                          { "text": "Number" },
                          { "text": "FOUR" }
                        ]
                      }
                    }
                  ]
                }
              }
            }
          },
          {
            "type": {
              "array_type": {
                "element_count": {
                  "constant_reference": {
                    "source_name": [
                      { "text": "Number" },
                      { "text": "FOUR" }
                    ]
                  }
                }
              }
            },
            "name": { "name": { "text": "array_size_four" } }
          }
        ]
      }
    },
    {
      "enumeration": {
        "value": [
          {
            "name": { "name": { "text": "FOUR" } },
            "value": { "constant": { "value": "4" } }
          },
          {
            "name": { "name": { "text": "EIGHT" } },
            "value": {
              "function": {
                "function": "ADDITION",
                "args": [
                  {
                    "constant_reference": {
                      "source_name": [ { "text": "FOUR" } ]
                    }
                  },
                  {
                    "constant_reference": {
                      "source_name": [
                        { "text": "Number" },
                        { "text": "FOUR" }
                      ]
                    }
                  }
                ]
              }
            }
          },
          {
            "name": { "name": { "text": "SIXTEEN" } },
            "value": {
              "function": {
                "function": "MULTIPLICATION",
                "args": [
                  {
                    "constant_reference": {
                      "source_name": [
                        { "text": "Number" },
                        { "text": "FOUR" }
                      ]
                    }
                  },
                  {
                    "constant_reference": {
                      "source_name": [ { "text": "FOUR" } ]
                    }
                  }
                ]
              }
            }
          },
          {
            "name": { "name": { "text": "INVALID" } },
            "value": {
              "constant_reference": {
                "source_name": [
                  { "text": "Number" },
                  { "text": "NaN" },
                  { "text": "FOUR" }
                ]
              }
            }
          }
        ]
      }
    }
  ]
}

===
Using Type.constants in expressions
---
struct Foo:
  0 [+Bar.four]  UInt  length_four
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": { "name": { "text": "length_four" } },
            "location": {
              "size": {
                "constant_reference": {
                  "source_name": [ { "text": "Bar" }, { "text": "four" } ]
                }
              }
            }
          }
        ]
      }
    }
  ]
}

===
using Type.Subtype
---
struct Foo:
  0 [+0]  Bar.Baz  bar_baz
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "type": {
              "atomic_type": {
                "reference": {
                  "source_name": [ { "text": "Bar" }, { "text": "Baz" } ]
                }
              }
            },
            "name": { "name": { "text": "bar_baz" } }
          }
        ]
      }
    }
  ]
}

===
module.Type
---
struct Foo:
  0 [+0]  bar.Baz  bar_baz
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "type": {
              "atomic_type": {
                "reference": {
                  "source_name": [ { "text": "bar" }, { "text": "Baz" } ]
                }
              }
            },
            "name": { "name": { "text": "bar_baz" } }
          }
        ]
      }
    }
  ]
}

===
module.Type.ENUM_VALUE
---
struct Foo:
  bar.Baz.QUX [+0]  UInt  i
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": { "name": { "text": "i" } },
            "location": {
              "start": {
                "constant_reference": {
                  "source_name": [
                    { "text": "bar" },
                    { "text": "Baz" },
                    { "text": "QUX" }
                  ]
                }
              }
            }
          }
        ]
      }
    }
  ]
}

===
field attributes
---
struct Foo:
  0 [+1]  UInt  field  [fixed_size: true]
    [size: 1]
  1 [+2]  UInt  field2
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": {
              "name": {
                "text": "field"
              }
            },
            "attribute": [
              {
                "name": {
                  "text": "fixed_size"
                },
                "value": {
                  "expression": {
                    "boolean_constant": {
                      "value": true
                    }
                  }
                }
              },
              {
                "name": {
                  "text": "size"
                },
                "value": {
                  "expression": {
                    "constant": {
                      "value": "1"
                    }
                  }
                }
              }
            ]
          },
          {
            "name": {
              "name": {
                "text": "field2"
              }
            }
          }
        ]
      },
      "name": {
        "name": {
          "text": "Foo"
        }
      }
    }
  ]
}

===
enum attribute
---
enum Foo:
  [fixed_size: false]
  NAME = 1
---
{
  "type": [
    {
      "enumeration": {
        "value": [ { "name": { "name": { "text": "NAME" } } } ]
      },
      "name": { "name": { "text": "Foo" } },
      "attribute": [
        {
          "name": { "text": "fixed_size" },
          "value": {
            "expression": { "boolean_constant": { "value": false } }
          }
        }
      ]
    }
  ]
}

===
string attribute
---
[abc: "abc"]
[bs: "abc\\"]
[bsbs: "abc\\\\"]
[nl: "abc\nd"]
[q: "abc\"d"]
[qq: "abc\"\""]
---
{
  "attribute": [
    {
      "name": { "text": "abc" },
      "value": { "string_constant": { "text": "abc" } }
    },
    {
      "name": { "text": "bs" },
      "value": { "string_constant": { "text": "abc\\" } }
    },
    {
      "name": { "text": "bsbs" },
      "value": { "string_constant": { "text": "abc\\\\" } }
    },
    {
      "name": { "text": "nl" },
      "value": { "string_constant": { "text": "abc\nd" } }
    },
    {
      "name": { "text": "q" },
      "value": { "string_constant": { "text": "abc\"d" } }
    },
    {
      "name": { "text": "qq" },
      "value": { "string_constant": { "text": "abc\"\"" } }
    }
  ]
}

===
back-end-specific attribute
---
[(cpp) namespace: "a::b::c"]
---
{
  "attribute": [
    {
      "name": { "text": "namespace" },
      "value": { "string_constant": { "text": "a::b::c" } },
      "back_end": { "text": "cpp" }
    }
  ]
}

===
documentation
---
-- module doc
--
-- module doc 2
struct Foo:
  -- foo doc
  -- foo doc 2
  0 [+1]  UInt  bar  -- bar inline doc
    -- bar continued doc
    -- bar continued doc 2
enum Baz:
  -- baz doc
  -- baz doc 2
  QUX = 1  -- qux inline doc
    -- qux continued doc
    -- qux continued doc 2
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": {
              "name": {
                "text": "bar"
              }
            },
            "documentation": [
              {
                "text": "bar inline doc"
              },
              {
                "text": "bar continued doc"
              },
              {
                "text": "bar continued doc 2"
              }
            ]
          }
        ]
      },
      "name": {
        "name": {
          "text": "Foo"
        }
      },
      "documentation": [
        {
          "text": "foo doc"
        },
        {
          "text": "foo doc 2"
        }
      ]
    },
    {
      "enumeration": {
        "value": [
          {
            "name": {
              "name": {
                "text": "QUX"
              }
            },
            "documentation": [
              {
                "text": "qux inline doc"
              },
              {
                "text": "qux continued doc"
              },
              {
                "text": "qux continued doc 2"
              }
            ]
          }
        ]
      },
      "name": {
        "name": {
          "text": "Baz"
        }
      },
      "documentation": [
        {
          "text": "baz doc"
        },
        {
          "text": "baz doc 2"
        }
      ]
    }
  ],
  "documentation": [
    {
      "text": "module doc"
    },
    {
      "text": ""
    },
    {
      "text": "module doc 2"
    }
  ]
}

===
inline enum
---
struct Foo:
  0 [+1]  enum  baz_qux_gibble (bqg):
    [q: 5]
    BAR = 1
    FOO = 2
bits Bar:
  0 [+1]  enum  baz_qux_gibble (bqg):
    [q: 5]
    BAR = 1
    FOO = 2
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "type": {
              "atomic_type": {
                "reference": {
                  "source_name": [ { "text": "BazQuxGibble" } ],
                  "is_local_name": true
                }
              }
            },
            "name": { "name": { "text": "baz_qux_gibble" } },
            "abbreviation": { "text": "bqg" },
            "attribute": [
              {
                "name": { "text": "q" },
                "value": { "expression": { "constant": { "value": "5" } } }
              }
            ]
          }
        ]
      },
      "name": { "name": { "text": "Foo" } },
      "subtype": [
        {
          "enumeration": {
            "value": [
              {
                "name": { "name": { "text": "BAR" } },
                "value": { "constant": { "value": "1" } }
              },
              {
                "name": { "name": { "text": "FOO" } },
                "value": { "constant": { "value": "2" } }
              }
            ]
          },
          "name": { "name": { "text": "BazQuxGibble" } }
        }
      ]
    },
    {
      "structure": {
        "field": [
          {
            "type": {
              "atomic_type": {
                "reference": {
                  "source_name": [ { "text": "BazQuxGibble" } ],
                  "is_local_name": true
                }
              }
            },
            "name": { "name": { "text": "baz_qux_gibble" } },
            "abbreviation": { "text": "bqg" },
            "attribute": [
              {
                "name": { "text": "q" },
                "value": { "expression": { "constant": { "value": "5" } } }
              }
            ]
          }
        ]
      },
      "name": { "name": { "text": "Bar" } },
      "subtype": [
        {
          "enumeration": {
            "value": [
              {
                "name": { "name": { "text": "BAR" } },
                "value": { "constant": { "value": "1" } }
              },
              {
                "name": { "name": { "text": "FOO" } },
                "value": { "constant": { "value": "2" } }
              }
            ]
          },
          "name": { "name": { "text": "BazQuxGibble" } }
        }
      ]
    }
  ]
}

===
inline struct
---
struct Foo:
  0 [+1]  struct  baz_qux_gibble (bqg):
    [q: 5]
    0 [+1]  UInt  bar
    1 [+1]  UInt  foo
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "type": {
              "atomic_type": {
                "reference": {
                  "source_name": [ { "text": "BazQuxGibble" } ],
                  "is_local_name": true
                }
              }
            },
            "name": { "name": { "text": "baz_qux_gibble" } },
            "abbreviation": { "text": "bqg" },
            "attribute": [
              {
                "name": { "text": "q" },
                "value": { "expression": { "constant": { "value": "5" } } }
              }
            ]
          }
        ]
      },
      "name": { "name": { "text": "Foo" } },
      "subtype": [
        {
          "structure": {
            "field": [
              {
                "type": {
                  "atomic_type": {
                    "reference": { "source_name": [ { "text": "UInt" } ] }
                  }
                },
                "name": { "name": { "text": "bar" } }
              },
              {
                "type": {
                  "atomic_type": {
                    "reference": { "source_name": [ { "text": "UInt" } ] }
                  }
                },
                "name": { "name": { "text": "foo" } }
              }
            ]
          },
          "name": { "name": { "text": "BazQuxGibble" } }
        }
      ]
    }
  ]
}

===
inline bits
---
struct Foo:
  0 [+1]  bits  baz_qux_gibble (bqg):
    [q: 5]
    0 [+1]  UInt  bar
    1 [+1]  UInt  foo
bits Bar:
  0 [+8]  bits  baz_qux_gibble (bqg):
    [q: 5]
    0 [+1]  UInt  bar
    1 [+1]  UInt  foo
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "type": {
              "atomic_type": {
                "reference": {
                  "source_name": [
                    {
                      "text": "BazQuxGibble"
                    }
                  ]
                }
              }
            },
            "name": {
              "name": {
                "text": "baz_qux_gibble"
              }
            },
            "abbreviation": {
              "text": "bqg"
            },
            "attribute": [
              {
                "name": {
                  "text": "q"
                },
                "value": {
                  "expression": {
                    "constant": {
                      "value": "5"
                    }
                  }
                }
              }
            ]
          }
        ]
      },
      "name": {
        "name": {
          "text": "Foo"
        }
      },
      "subtype": [
        {
          "structure": {
            "field": [
              {
                "type": {
                  "atomic_type": {
                    "reference": {
                      "source_name": [
                        {
                          "text": "UInt"
                        }
                      ]
                    }
                  }
                },
                "name": {
                  "name": {
                    "text": "bar"
                  }
                }
              },
              {
                "type": {
                  "atomic_type": {
                    "reference": {
                      "source_name": [
                        {
                          "text": "UInt"
                        }
                      ]
                    }
                  }
                },
                "name": {
                  "name": {
                    "text": "foo"
                  }
                }
              }
            ]
          },
          "name": {
            "name": {
              "text": "BazQuxGibble"
            }
          }
        }
      ]
    },
    {
      "structure": {
        "field": [
          {
            "type": {
              "atomic_type": {
                "reference": {
                  "source_name": [
                    {
                      "text": "BazQuxGibble"
                    }
                  ]
                }
              }
            },
            "name": {
              "name": {
                "text": "baz_qux_gibble"
              }
            },
            "abbreviation": {
              "text": "bqg"
            },
            "attribute": [
              {
                "name": {
                  "text": "q"
                },
                "value": {
                  "expression": {
                    "constant": {
                      "value": "5"
                    }
                  }
                }
              }
            ]
          }
        ]
      },
      "name": {
        "name": {
          "text": "Bar"
        }
      },
      "subtype": [
        {
          "structure": {
            "field": [
              {
                "type": {
                  "atomic_type": {
                    "reference": {
                      "source_name": [
                        {
                          "text": "UInt"
                        }
                      ]
                    }
                  }
                },
                "name": {
                  "name": {
                    "text": "bar"
                  }
                }
              },
              {
                "type": {
                  "atomic_type": {
                    "reference": {
                      "source_name": [
                        {
                          "text": "UInt"
                        }
                      ]
                    }
                  }
                },
                "name": {
                  "name": {
                    "text": "foo"
                  }
                }
              }
            ]
          },
          "name": {
            "name": {
              "text": "BazQuxGibble"
            }
          }
        }
      ]
    }
  ]
}

===
subfield
---
struct Foo:
  foo.bar [+1]  UInt  x
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": {
              "name": {
                "text": "x"
              }
            },
            "location": {
              "start": {
                "field_reference": {
                  "path": [
                    {
                      "source_name": [
                        {
                          "text": "foo"
                        }
                      ]
                    },
                    {
                      "source_name": [
                        {
                          "text": "bar"
                        }
                      ]
                    }
                  ]
                }
              }
            }
          }
        ]
      },
      "name": {
        "name": {
          "text": "Foo"
        }
      }
    }
  ]
}

===
anonymous bits
---
struct Foo:
  0 [+1]  bits:
    31 [+1]    enum  high_bit:
      OFF = 0
      ON  = 1
    0 [+1]     Flag  low_bit
    if false:
      16 [+1]  UInt  mid_high
      15 [+1]  UInt  mid_low
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": {
              "is_anonymous": true
            },
            "location": {
              "start": {
                "constant": {
                  "value": "0"
                }
              },
              "size": {
                "constant": {
                  "value": "1"
                }
              }
            }
          }
        ]
      },
      "name": {
        "name": {
          "text": "Foo"
        }
      },
      "subtype": [
        {
          "structure": {
            "field": [
              {
                "name": {
                  "name": {
                    "text": "high_bit"
                  }
                }
              },
              {
                "name": {
                  "name": {
                    "text": "low_bit"
                  }
                }
              },
              {
                "name": {
                  "name": {
                    "text": "mid_high"
                  }
                },
                "existence_condition": {
                  "boolean_constant": {
                    "value": false
                  }
                }
              },
              {
                "name": {
                  "name": {
                    "text": "mid_low"
                  }
                },
                "existence_condition": {
                  "boolean_constant": {
                    "value": false
                  }
                }
              }
            ]
          },
          "name": { "is_anonymous": true }
        },
        {
          "enumeration": {
            "value": [
              {
                "name": { "name": { "text": "OFF" } },
                "value": { "constant": { "value": "0" } }
              },
              {
                "name": { "name": { "text": "ON" } },
                "value": { "constant": { "value": "1" } }
              }
            ]
          },
          "name": { "name": { "text": "HighBit" } }
        }
      ]
    }
  ]
}

===
explicit type size
---
struct Foo:
  0 [+1]  Bar:8  bar
---
{
  "type": [
    {
      "structure": {
        "field": [
          { "type": { "size_in_bits": { "constant": { "value": "8" } } } }
        ]
      },
      "name": { "name": { "text": "Foo" } }
    }
  ]
}

===
import
---
import "xyz.emb" as yqf
---
{
  "foreign_import": [
    { "file_name": { "text": "" }, "local_name": { "text": "" } },
    { "file_name": { "text": "xyz.emb" }, "local_name": { "text": "yqf" } }
  ]
}

===
empty file
---
---
{
  "foreign_import": [
    {
      "file_name": {
        "text": "",
        "source_location": {
          "start": { "line": 1, "column": 1 },
          "end": { "line": 1, "column": 1 }
        }
      },
      "local_name": {
        "text": "",
        "source_location": {
          "start": { "line": 1, "column": 1 },
          "end": { "line": 1, "column": 1 }
        }
      },
      "source_location": {
        "start": { "line": 1, "column": 1 },
        "end": { "line": 1, "column": 1 }
      }
    }
  ],
  "source_location": {
    "start": { "line": 1, "column": 1 },
    "end": { "line": 1, "column": 1 }
  }
}

===
existence_condition on unconditional field
---
struct Foo:
  0 [+1]  UInt  bar
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": { "name": { "text": "bar" } },
            "existence_condition": { "boolean_constant": { "value": true } }
          }
        ]
      }
    }
  ]
}

===
conditional struct fields
---
struct Foo:
  if true == false:
    0 [+1]  UInt  bar
    1 [+1]  bits:
      0 [+1]  UInt  xx
      1 [+1]  UInt  yy
    2 [+1]  enum  baz:
      XX = 1
      YY = 2
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": { "name": { "text": "bar" } },
            "existence_condition": {
              "function": {
                "function": "EQUALITY",
                "args": [
                  { "boolean_constant": { "value": true } },
                  { "boolean_constant": { "value": false } }
                ]
              }
            }
          },
          {
            "existence_condition": {
              "function": {
                "function": "EQUALITY",
                "args": [
                  { "boolean_constant": { "value": true } },
                  { "boolean_constant": { "value": false } }
                ]
              }
            }
          },
          {
            "name": { "name": { "text": "baz" } },
            "existence_condition": {
              "function": {
                "function": "EQUALITY",
                "args": [
                  { "boolean_constant": { "value": true } },
                  { "boolean_constant": { "value": false } }
                ]
              }
            }
          }
        ]
      },
      "subtype": [
        {
          "structure": {
            "field": [
              {
                "name": { "name": { "text": "xx" } },
                "existence_condition": { "boolean_constant": { "value": true } }
              }
            ]
          }
        }
      ]
    }
  ]
}

===
negative condition
---
struct Foo:
  if true != false:
    0 [+1]  UInt  bar
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": { "name": { "text": "bar" } },
            "existence_condition": {
              "function": {
                "function": "INEQUALITY",
                "args": [
                  { "boolean_constant": { "value": true } },
                  { "boolean_constant": { "value": false } }
                ]
              }
            }
          }
        ]
      }
    }
  ]
}

===
conditional bits fields
---
bits Foo:
  if true == false:
    0 [+1]  UInt  bar
    1 [+1]  enum  baz:
      XX = 1
      YY = 2
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": { "name": { "text": "bar" } },
            "existence_condition": {
              "function": {
                "function": "EQUALITY",
                "args": [
                  { "boolean_constant": { "value": true } },
                  { "boolean_constant": { "value": false } }
                ]
              }
            }
          },
          {
            "name": { "name": { "text": "baz" } },
            "existence_condition": {
              "function": {
                "function": "EQUALITY",
                "args": [
                  { "boolean_constant": { "value": true } },
                  { "boolean_constant": { "value": false } }
                ]
              }
            }
          }
        ]
      }
    }
  ]
}

===
conditional with logical and
---
struct Foo:
  if true && false:
    0 [+1]  UInt  bar
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": {
              "name": {
                "text": "bar"
              }
            },
            "existence_condition": {
              "function": {
                "function": "AND",
                "args": [
                  {
                    "boolean_constant": {
                      "value": true
                    }
                  },
                  {
                    "boolean_constant": {
                      "value": false
                    }
                  }
                ]
              }
            }
          }
        ]
      }
    }
  ]
}

===
conditional with logical or
---
struct Foo:
  if true || false:
    0 [+1]  UInt  bar
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": {
              "name": {
                "text": "bar"
              }
            },
            "existence_condition": {
              "function": {
                "function": "OR",
                "args": [
                  {
                    "boolean_constant": {
                      "value": true
                    }
                  },
                  {
                    "boolean_constant": {
                      "value": false
                    }
                  }
                ]
              }
            }
          }
        ]
      }
    }
  ]
}

===
conditional with multiple logical ands
---
struct Foo:
  if true && false && true:
    0 [+1]  UInt  bar
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": {
              "name": {
                "text": "bar"
              }
            },
            "existence_condition": {
              "function": {
                "function": "AND",
                "args": [
                  {
                    "function": {
                      "function": "AND",
                      "args": [
                        {
                          "boolean_constant": {
                            "value": true
                          }
                        },
                        {
                          "boolean_constant": {
                            "value": false
                          }
                        }
                      ]
                    }
                  },
                  {
                    "boolean_constant": {
                      "value": true
                    }
                  }
                ]
              }
            }
          }
        ]
      }
    }
  ]
}

===
conditional with multiple logical ors
---
struct Foo:
  if true || false || true:
    0 [+1]  UInt  bar
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": {
              "name": {
                "text": "bar"
              }
            },
            "existence_condition": {
              "function": {
                "function": "OR",
                "args": [
                  {
                    "function": {
                      "function": "OR",
                      "args": [
                        {
                          "boolean_constant": {
                            "value": true
                          }
                        },
                        {
                          "boolean_constant": {
                            "value": false
                          }
                        }
                      ]
                    }
                  },
                  {
                    "boolean_constant": {
                      "value": true
                    }
                  }
                ]
              }
            }
          }
        ]
      }
    }
  ]
}

===
conditional with comparisons and logical or
---
struct Foo:
  if 5 == 6 || 6 == 6:
    0 [+1]  UInt  bar
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": {
              "name": {
                "text": "bar"
              }
            },
            "existence_condition": {
              "function": {
                "function": "OR",
                "args": [
                  {
                    "function": {
                      "function": "EQUALITY",
                      "args": [
                        {
                          "constant": {
                            "value": "5"
                          }
                        },
                        {
                          "constant": {
                            "value": "6"
                          }
                        }
                      ]
                    }
                  },
                  {
                    "function": {
                      "function": "EQUALITY",
                      "args": [
                        {
                          "constant": {
                            "value": "6"
                          }
                        },
                        {
                          "constant": {
                            "value": "6"
                          }
                        }
                      ]
                    }
                  }
                ]
              }
            }
          }
        ]
      }
    }
  ]
}

===
conditional with or-of-ands
---
struct Foo:
  if true || (false && true):
    0 [+1]  UInt  bar
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": {
              "name": {
                "text": "bar"
              }
            },
            "existence_condition": {
              "function": {
                "function": "OR",
                "args": [
                  {
                    "boolean_constant": {
                      "value": true
                    }
                  },
                  {
                    "function": {
                      "function": "AND",
                      "args": [
                        {
                          "boolean_constant": {
                            "value": false
                          }
                        },
                        {
                          "boolean_constant": {
                            "value": true
                          }
                        }
                      ]
                    }
                  }
                ]
              }
            }
          }
        ]
      }
    }
  ]
}

===
less-than comparison
---
struct Foo:
  if 1 < 2:
    0 [+1]  UInt  bar
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": {
              "name": {
                "text": "bar"
              }
            },
            "existence_condition": {
              "function": {
                "function": "LESS",
                "args": [
                  {
                    "constant": {
                      "value": "1"
                    }
                  },
                  {
                    "constant": {
                      "value": "2"
                    }
                  }
                ]
              }
            }
          }
        ]
      }
    }
  ]
}

===
less-than-or-equal comparison
---
struct Foo:
  if 1 <= 2:
    0 [+1]  UInt  bar
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": {
              "name": {
                "text": "bar"
              }
            },
            "existence_condition": {
              "function": {
                "function": "LESS_OR_EQUAL",
                "args": [
                  {
                    "constant": {
                      "value": "1"
                    }
                  },
                  {
                    "constant": {
                      "value": "2"
                    }
                  }
                ]
              }
            }
          }
        ]
      }
    }
  ]
}

===
greater-than comparison
---
struct Foo:
  if 1 > 2:
    0 [+1]  UInt  bar
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": {
              "name": {
                "text": "bar"
              }
            },
            "existence_condition": {
              "function": {
                "function": "GREATER",
                "args": [
                  {
                    "constant": {
                      "value": "1"
                    }
                  },
                  {
                    "constant": {
                      "value": "2"
                    }
                  }
                ]
              }
            }
          }
        ]
      }
    }
  ]
}

===
greater-than-or-equal comparison
---
struct Foo:
  if 1 >= 2:
    0 [+1]  UInt  bar
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": {
              "name": {
                "text": "bar"
              }
            },
            "existence_condition": {
              "function": {
                "function": "GREATER_OR_EQUAL",
                "args": [
                  {
                    "constant": {
                      "value": "1"
                    }
                  },
                  {
                    "constant": {
                      "value": "2"
                    }
                  }
                ]
              }
            }
          }
        ]
      }
    }
  ]
}

===
chained less-than comparison
---
struct Foo:
  if 1 < 2 < 3:
    0 [+1]  UInt  bar
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": {
              "name": {
                "text": "bar"
              }
            },
            "existence_condition": {
              "function": {
                "function": "AND",
                "args": [
                  {
                    "function": {
                      "function": "LESS",
                      "args": [
                        {
                          "constant": {
                            "value": "1"
                          }
                        },
                        {
                          "constant": {
                            "value": "2"
                          }
                        }
                      ]
                    }
                  },
                  {
                    "function": {
                      "function": "LESS",
                      "args": [
                        {
                          "constant": {
                            "value": "2"
                          }
                        },
                        {
                          "constant": {
                            "value": "3"
                          }
                        }
                      ]
                    }
                  }
                ]
              }
            }
          }
        ]
      }
    }
  ]
}

===
chained greater-than comparison
---
struct Foo:
  if 1 > 2 > 3:
    0 [+1]  UInt  bar
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": {
              "name": {
                "text": "bar"
              }
            },
            "existence_condition": {
              "function": {
                "function": "AND",
                "args": [
                  {
                    "function": {
                      "function": "GREATER",
                      "args": [
                        {
                          "constant": {
                            "value": "1"
                          }
                        },
                        {
                          "constant": {
                            "value": "2"
                          }
                        }
                      ]
                    }
                  },
                  {
                    "function": {
                      "function": "GREATER",
                      "args": [
                        {
                          "constant": {
                            "value": "2"
                          }
                        },
                        {
                          "constant": {
                            "value": "3"
                          }
                        }
                      ]
                    }
                  }
                ]
              }
            }
          }
        ]
      }
    }
  ]
}

===
longer chained less-than comparison
---
struct Foo:
  if 1 < 2 < 3 <= 4:
    0 [+1]  UInt  bar
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": {
              "name": {
                "text": "bar"
              }
            },
            "existence_condition": {
              "function": {
                "function": "AND",
                "args": [
                  {
                    "function": {
                      "function": "AND",
                      "args": [
                        {
                          "function": {
                            "function": "LESS",
                            "args": [
                              {
                                "constant": {
                                  "value": "1"
                                }
                              },
                              {
                                "constant": {
                                  "value": "2"
                                }
                              }
                            ]
                          }
                        },
                        {
                          "function": {
                            "function": "LESS",
                            "args": [
                              {
                                "constant": {
                                  "value": "2"
                                }
                              },
                              {
                                "constant": {
                                  "value": "3"
                                }
                              }
                            ]
                          }
                        }
                      ]
                    }
                  },
                  {
                    "function": {
                      "function": "LESS_OR_EQUAL",
                      "args": [
                        {
                          "constant": {
                            "value": "3"
                          }
                        },
                        {
                          "constant": {
                            "value": "4"
                          }
                        }
                      ]
                    }
                  }
                ]
              }
            }
          }
        ]
      }
    }
  ]
}

===
longer chained greater-than comparison
---
struct Foo:
  if 1 > 2 > 3 >= 4:
    0 [+1]  UInt  bar
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": {
              "name": {
                "text": "bar"
              }
            },
            "existence_condition": {
              "function": {
                "function": "AND",
                "args": [
                  {
                    "function": {
                      "function": "AND",
                      "args": [
                        {
                          "function": {
                            "function": "GREATER",
                            "args": [
                              {
                                "constant": {
                                  "value": "1"
                                }
                              },
                              {
                                "constant": {
                                  "value": "2"
                                }
                              }
                            ]
                          }
                        },
                        {
                          "function": {
                            "function": "GREATER",
                            "args": [
                              {
                                "constant": {
                                  "value": "2"
                                }
                              },
                              {
                                "constant": {
                                  "value": "3"
                                }
                              }
                            ]
                          }
                        }
                      ]
                    }
                  },
                  {
                    "function": {
                      "function": "GREATER_OR_EQUAL",
                      "args": [
                        {
                          "constant": {
                            "value": "3"
                          }
                        },
                        {
                          "constant": {
                            "value": "4"
                          }
                        }
                      ]
                    }
                  }
                ]
              }
            }
          }
        ]
      }
    }
  ]
}

===
chained less-than and equal comparison
---
struct Foo:
  if 1 < 2 == 3:
    0 [+1]  UInt  bar
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": {
              "name": {
                "text": "bar"
              }
            },
            "existence_condition": {
              "function": {
                "function": "AND",
                "args": [
                  {
                    "function": {
                      "function": "LESS",
                      "args": [
                        {
                          "constant": {
                            "value": "1"
                          }
                        },
                        {
                          "constant": {
                            "value": "2"
                          }
                        }
                      ]
                    }
                  },
                  {
                    "function": {
                      "function": "EQUALITY",
                      "args": [
                        {
                          "constant": {
                            "value": "2"
                          }
                        },
                        {
                          "constant": {
                            "value": "3"
                          }
                        }
                      ]
                    }
                  }
                ]
              }
            }
          }
        ]
      }
    }
  ]
}

===
chained greater-than and equal comparison
---
struct Foo:
  if 1 > 2 == 3:
    0 [+1]  UInt  bar
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": {
              "name": {
                "text": "bar"
              }
            },
            "existence_condition": {
              "function": {
                "function": "AND",
                "args": [
                  {
                    "function": {
                      "function": "GREATER",
                      "args": [
                        {
                          "constant": {
                            "value": "1"
                          }
                        },
                        {
                          "constant": {
                            "value": "2"
                          }
                        }
                      ]
                    }
                  },
                  {
                    "function": {
                      "function": "EQUALITY",
                      "args": [
                        {
                          "constant": {
                            "value": "2"
                          }
                        },
                        {
                          "constant": {
                            "value": "3"
                          }
                        }
                      ]
                    }
                  }
                ]
              }
            }
          }
        ]
      }
    }
  ]
}

===
chained equal and less-than comparison
---
struct Foo:
  if 1 == 2 < 3:
    0 [+1]  UInt  bar
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": {
              "name": {
                "text": "bar"
              }
            },
            "existence_condition": {
              "function": {
                "function": "AND",
                "args": [
                  {
                    "function": {
                      "function": "EQUALITY",
                      "args": [
                        {
                          "constant": {
                            "value": "1"
                          }
                        },
                        {
                          "constant": {
                            "value": "2"
                          }
                        }
                      ]
                    }
                  },
                  {
                    "function": {
                      "function": "LESS",
                      "args": [
                        {
                          "constant": {
                            "value": "2"
                          }
                        },
                        {
                          "constant": {
                            "value": "3"
                          }
                        }
                      ]
                    }
                  }
                ]
              }
            }
          }
        ]
      }
    }
  ]
}

===
chained equal and greater-than comparison
---
struct Foo:
  if 1 == 2 > 3:
    0 [+1]  UInt  bar
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": {
              "name": {
                "text": "bar"
              }
            },
            "existence_condition": {
              "function": {
                "function": "AND",
                "args": [
                  {
                    "function": {
                      "function": "EQUALITY",
                      "args": [
                        {
                          "constant": {
                            "value": "1"
                          }
                        },
                        {
                          "constant": {
                            "value": "2"
                          }
                        }
                      ]
                    }
                  },
                  {
                    "function": {
                      "function": "GREATER",
                      "args": [
                        {
                          "constant": {
                            "value": "2"
                          }
                        },
                        {
                          "constant": {
                            "value": "3"
                          }
                        }
                      ]
                    }
                  }
                ]
              }
            }
          }
        ]
      }
    }
  ]
}

===
chained equality comparison
---
struct Foo:
  if 1 == 2 == 3:
    0 [+1]  UInt  bar
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": {
              "name": {
                "text": "bar"
              }
            },
            "existence_condition": {
              "function": {
                "function": "AND",
                "args": [
                  {
                    "function": {
                      "function": "EQUALITY",
                      "args": [
                        {
                          "constant": {
                            "value": "1"
                          }
                        },
                        {
                          "constant": {
                            "value": "2"
                          }
                        }
                      ]
                    }
                  },
                  {
                    "function": {
                      "function": "EQUALITY",
                      "args": [
                        {
                          "constant": {
                            "value": "2"
                          }
                        },
                        {
                          "constant": {
                            "value": "3"
                          }
                        }
                      ]
                    }
                  }
                ]
              }
            }
          }
        ]
      }
    }
  ]
}

===
choice operator
---
struct Foo:
  true ? 0 : 1 [+1]  UInt  bar
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": {
              "name": {
                "text": "bar"
              }
            },
            "location": {
              "start": {
                "function": {
                  "function": "CHOICE",
                  "args": [
                    {
                      "boolean_constant": {
                        "value": true
                      }
                    },
                    {
                      "constant": {
                        "value": "0"
                      }
                    },
                    {
                      "constant": {
                        "value": "1"
                      }
                    }
                  ]
                }
              }
            }
          }
        ]
      }
    }
  ]
}

===
max function
---
struct Foo:
  $max()               [+1]  UInt  no_arg
  $max(0)              [+1]  UInt  one_arg
  $max(2 * 3)          [+1]  UInt  mul_arg
  $max(2, 3)           [+1]  UInt  two_arg
  $max(2, 3, 4, 5, 6)  [+1]  UInt  five_arg
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": {
              "name": {
                "text": "no_arg"
              }
            },
            "location": {
              "start": {
                "function": {
                  "function": "MAXIMUM"
                }
              }
            }
          },
          {
            "name": {
              "name": {
                "text": "one_arg"
              }
            },
            "location": {
              "start": {
                "function": {
                  "function": "MAXIMUM",
                  "args": [
                    {
                      "constant": {
                        "value": "0"
                      }
                    }
                  ]
                }
              }
            }
          },
          {
            "name": {
              "name": {
                "text": "mul_arg"
              }
            },
            "location": {
              "start": {
                "function": {
                  "function": "MAXIMUM",
                  "args": [
                    {
                      "function": {
                        "function": "MULTIPLICATION",
                        "args": [
                          {
                            "constant": {
                              "value": "2"
                            }
                          },
                          {
                            "constant": {
                              "value": "3"
                            }
                          }
                        ]
                      }
                    }
                  ]
                }
              }
            }
          },
          {
            "name": {
              "name": {
                "text": "two_arg"
              }
            },
            "location": {
              "start": {
                "function": {
                  "function": "MAXIMUM",
                  "args": [
                    {
                      "constant": {
                        "value": "2"
                      }
                    },
                    {
                      "constant": {
                        "value": "3"
                      }
                    }
                  ]
                }
              }
            }
          },
          {
            "name": {
              "name": {
                "text": "five_arg"
              }
            },
            "location": {
              "start": {
                "function": {
                  "function": "MAXIMUM",
                  "args": [
                    {
                      "constant": {
                        "value": "2"
                      }
                    },
                    {
                      "constant": {
                        "value": "3"
                      }
                    },
                    {
                      "constant": {
                        "value": "4"
                      }
                    },
                    {
                      "constant": {
                        "value": "5"
                      }
                    },
                    {
                      "constant": {
                        "value": "6"
                      }
                    }
                  ]
                }
              }
            }
          }
        ]
      }
    }
  ]
}

===
has function
---
struct Foo:
  if $present(x):
    0 [+1]  UInt  field
  if $present(x.y.z):
    0 [+1]  UInt  field2
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": {
              "name": {
                "text": "field"
              }
            },
            "existence_condition": {
              "function": {
                "function": "PRESENCE",
                "args": [
                  {
                    "field_reference": {
                      "path": [
                        {
                          "source_name": [
                            {
                              "text": "x"
                            }
                          ]
                        }
                      ]
                    }
                  }
                ]
              }
            }
          },
          {
            "name": {
              "name": {
                "text": "field2"
              }
            },
            "existence_condition": {
              "function": {
                "function": "PRESENCE",
                "args": [
                  {
                    "field_reference": {
                      "path": [
                        {
                          "source_name": [
                            {
                              "text": "x"
                            }
                          ]
                        },
                        {
                          "source_name": [
                            {
                              "text": "y"
                            }
                          ]
                        },
                        {
                          "source_name": [
                            {
                              "text": "z"
                            }
                          ]
                        }
                      ]
                    }
                  }
                ]
              }
            }
          }
        ]
      }
    }
  ]
}

===
upper_bound function
---
struct Foo:
  $upper_bound(0)    [+1]  UInt  one
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": {
              "name": {
                "text": "one"
              }
            },
            "location": {
              "start": {
                "function": {
                  "function": "UPPER_BOUND",
                  "args": [
                    {
                      "constant": {
                        "value": "0"
                      }
                    }
                  ]
                }
              }
            }
          }
        ]
      }
    }
  ]
}

===
lower_bound function
---
struct Foo:
  $lower_bound(0)    [+1]  UInt  one
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": {
              "name": {
                "text": "one"
              }
            },
            "location": {
              "start": {
                "function": {
                  "function": "LOWER_BOUND",
                  "args": [
                    {
                      "constant": {
                        "value": "0"
                      }
                    }
                  ]
                }
              }
            }
          }
        ]
      }
    }
  ]
}

===
struct addressable_unit
---
struct Foo:
  0 [+1]  UInt  size
---
{ "type": [ { "structure": {}, "addressable_unit": "BYTE" } ] }

===
bits addressable_unit
---
bits Foo:
  0 [+1]  UInt  size
---
{ "type": [ { "structure": {}, "addressable_unit": "BIT" } ] }

===
enum addressable_unit
---
enum Foo:
  BAR = 0
---
{ "type": [ { "enumeration": {}, "addressable_unit": "BIT" } ] }

===
type size source_location
---
struct Foo:
  0 [+4]  UInt:32  field
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "type": {
              "atomic_type": {
                "reference": { "source_name": [ { "text": "UInt" } ] }
              },
              "size_in_bits": {
                "source_location": {
                  "start": { "line": 3, "column": 15 },
                  "end": { "line": 3, "column": 18 }
                }
              },
              "source_location": {
                "start": { "line": 3, "column": 11 },
                "end": { "line": 3, "column": 18 }
              }
            },
            "name": { "name": { "text": "field" } }
          }
        ]
      }
    }
  ]
}

===
builtin references
---
external Foo:
  [requires: $is_statically_sized && $static_size_in_bits == 64]
---
{
  "type": [
    {
      "external": {},
      "attribute": [
        {
          "name": { "text": "requires" },
          "value": {
            "expression": {
              "function": {
                "args": [
                  {
                    "builtin_reference": {
                      "canonical_name": {
                        "module_file": "",
                        "object_path": [ "$is_statically_sized" ]
                      },
                      "source_name": [ { "text": "$is_statically_sized" } ]
                    }
                  },
                  {
                    "function": {
                      "args": [
                        {
                          "builtin_reference": {
                            "canonical_name": {
                              "module_file": "",
                              "object_path": [ "$static_size_in_bits" ]
                            },
                            "source_name": [
                              { "text": "$static_size_in_bits" }
                            ]
                          }
                        }
                      ]
                    }
                  }
                ]
              }
            }
          }
        }
      ]
    }
  ]
}

===
$next
---
struct Foo:
  $next [+0]  UInt  x
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "location": {
              "start": {
                "builtin_reference": { "source_name": [ { "text": "$next" } ] }
              }
            },
            "name": { "name": { "text": "x" } }
          }
        ]
      }
    }
  ]
}

===
virtual fields
---
struct Foo:
  let x = 10
bits Bar:
  let y = 100
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": { "name": { "text": "x" } },
            "read_transform": { "constant": { "value": "10" } }
          }
        ]
      }
    },
    {
      "structure": {
        "field": [
          {
            "name": { "name": { "text": "y" } },
            "read_transform": { "constant": { "value": "100" } }
          }
        ]
      }
    }
  ]
}

===
builtin fields
---
struct Foo:
  let x = $size_in_bytes
  let y = $max_size_in_bytes
  let z = $min_size_in_bytes
bits Bar:
  let x = $size_in_bits
  let y = $max_size_in_bits
  let z = $min_size_in_bits
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "name": { "name": { "text": "x" } },
            "read_transform": {
              "field_reference": {
                "path": [ { "source_name": [ { "text": "$size_in_bytes" } ] } ]
              }
            }
          },
          {
            "name": { "name": { "text": "y" } },
            "read_transform": {
              "field_reference": {
                "path": [
                  { "source_name": [ { "text": "$max_size_in_bytes" } ] }
                ]
              }
            }
          },
          {
            "name": { "name": { "text": "z" } },
            "read_transform": {
              "field_reference": {
                "path": [
                  { "source_name": [ { "text": "$min_size_in_bytes" } ] }
                ]
              }
            }
          }
        ]
      }
    },
    {
      "structure": {
        "field": [
          {
            "name": { "name": { "text": "x" } },
            "read_transform": {
              "field_reference": {
                "path": [ { "source_name": [ { "text": "$size_in_bits" } ] } ]
              }
            }
          },
          {
            "name": { "name": { "text": "y" } },
            "read_transform": {
              "field_reference": {
                "path": [
                  { "source_name": [ { "text": "$max_size_in_bits" } ] }
                ]
              }
            }
          },
          {
            "name": { "name": { "text": "z" } },
            "read_transform": {
              "field_reference": {
                "path": [
                  { "source_name": [ { "text": "$min_size_in_bits" } ] }
                ]
              }
            }
          }
        ]
      }
    }
  ]
}

===
parameterized type definitions
---
struct Foo(a: Flag, b: UInt:32):
  let x = 10
bits Bar(c: UInt:16):
  let y = 100
struct Baz():
  let x = 10
---
{
  "type": [
    {
      "runtime_parameter": [
        {
          "name": { "name": { "text": "a" } },
          "physical_type_alias": {
            "atomic_type": {
              "reference": { "source_name": [ { "text": "Flag" } ] }
            }
          }
        },
        {
          "name": { "name": { "text": "b" } },
          "physical_type_alias": {
            "atomic_type": {
              "reference": { "source_name": [ { "text": "UInt" } ] }
            },
            "size_in_bits": { "constant": { "value": "32" } }
          }
        }
      ]
    },
    {
      "runtime_parameter": [
        {
          "name": { "name": { "text": "c" } },
          "physical_type_alias": {
            "atomic_type": {
              "reference": { "source_name": [ { "text": "UInt" } ] }
            },
            "size_in_bits": { "constant": { "value": "16" } }
          }
        }
      ]
    },
    {}
  ]
}

===
parameterized type usages
---
struct Foo:
  0 [+1]  Two(1, 2)  two
  1 [+1]  One(3)     one
  2 [+1]  Zero()     zero
---
{
  "type": [
    {
      "structure": {
        "field": [
          {
            "type": {
              "atomic_type": {
                "reference": { "source_name": [ { "text": "Two" } ] },
                "runtime_parameter": [
                  { "constant": { "value": "1" } },
                  { "constant": { "value": "2" } }
                ]
              }
            },
            "name": { "name": { "text": "two" } }
          },
          {
            "type": {
              "atomic_type": {
                "reference": { "source_name": [ { "text": "One" } ] },
                "runtime_parameter": [ { "constant": { "value": "3" } } ]
              }
            },
            "name": { "name": { "text": "one" } }
          },
          {
            "type": {
              "atomic_type": {
                "reference": { "source_name": [ { "text": "Zero" } ] }
              }
            },
            "name": { "name": { "text": "zero" } }
          }
        ]
      }
    }
  ]
}

===
enum value attribute
---
enum Foo:
  BAR     = 1 [test: 0]
  BAZ     = 2
    [test: 1]
    [different: "test"]
  FOO_BAR = 4
    -- foo bar doc
    [test: 2]
  FOO_BAZ = 8 [test: 3] -- foo baz doc
  BAR_FOO = 16 [test: 4]
    -- bar foo doc
  BAZ_FOO = 32 -- baz foo doc
    [test: 5]
---
{
  "type": [
    {
      "enumeration": {
        "value": [
          {
            "name": { "name": { "text": "BAR" } },
            "attribute": [
              {
                "name": { "text": "test" },
                "value": { "expression": { "constant": { "value": "0" } } }
              }
            ]
          },
          {
            "name": { "name": { "text": "BAZ" } },
            "attribute": [
              {
                "name": { "text": "test" },
                "value": { "expression": { "constant": { "value": "1" } } }
              },
              {
                "name": { "text": "different" },
                "value": { "string_constant": { "text": "test" } }
              }
            ]
          },
          {
            "name": { "name": { "text": "FOO_BAR" } },
            "documentation": [ { "text": "foo bar doc" } ],
            "attribute": [
              {
                "name": { "text": "test" },
                "value": { "expression": { "constant": { "value": "2" } } }
              }
            ]
          },
          {
            "name": { "name": { "text": "FOO_BAZ" } },
            "documentation": [ { "text": "foo baz doc" } ],
            "attribute": [
              {
                "name": { "text": "test" },
                "value": { "expression": { "constant": { "value": "3" } } }
              }
            ]
          },
          {
            "name": { "name": { "text": "BAR_FOO" } },
            "documentation": [ { "text": "bar foo doc" } ],
            "attribute": [
              {
                "name": { "text": "test" },
                "value": { "expression": { "constant": { "value": "4" } } }
              }
            ]
          },
          {
            "name": { "name": { "text": "BAZ_FOO" } },
            "documentation": [ { "text": "baz foo doc" } ],
            "attribute": [
              {
                "name": { "text": "test" },
                "value": { "expression": { "constant": { "value": "5" } } }
              }
            ]
          }
        ]
      },
      "name": { "name": { "text": "Foo" } }
    }
  ]
}

"""


# For each test in _NEGATIVE_TEST_CASES, parsing should fail, and the failure
# should indicate the specified token.
_NEGATIVE_TEST_CASES = """
anonymous bits does not allow documentation
---
-- doc
---
struct Foo:
  0 [+1]  bits:
    -- doc
    0 [+2]  UInt  bar
===
anonymous bits does not allow subtypes
---
enum
---
struct Foo:
  0 [+1]  bits:
    enum Bar:
      X = 1
    0 [+2]  Bar  bar
"""


def _get_test_cases():
  test_case = collections.namedtuple("test_case", ["name", "parse_tree", "ir"])
  result = []
  for case in _TEST_CASES.split("==="):
    name, emb, ir_text = case.split("---")
    name = name.strip()
    try:
      ir = ir_data_utils.IrDataSerializer.from_json(ir_data.Module, ir_text)
    except Exception:
      print(name)
      raise
    parse_result = parser.parse_module(tokenizer.tokenize(emb, "")[0])
    assert not parse_result.error, "{}:\n{}".format(name, parse_result.error)
    result.append(test_case(name, parse_result.parse_tree, ir))
  return result


def _get_negative_test_cases():
  test_case = collections.namedtuple("test_case",
                                     ["name", "text", "error_token"])
  result = []
  for case in _NEGATIVE_TEST_CASES.split("==="):
    name, error_token, text = case.split("---")
    name = name.strip()
    error_token = error_token.strip()
    result.append(test_case(name, text, error_token))
  return result


def _check_source_location(source_location, path, min_start, max_end):
  """Performs sanity checks on a source_location field.

  Arguments:
    source_location: The source_location to check.
    path: The path, to use in error messages.
    min_start: A minimum value for source_location.start, or None.
    max_end: A maximum value for source_location.end, or None.

  Returns:
    A list of error messages, or an empty list if no errors.
  """
  if source_location.is_disjoint_from_parent:
    # If source_location.is_disjoint_from_parent, then this source_location is
    # allowed to be outside of the parent's source_location.
    return []

  result = []
  start = None
  end = None
  if not source_location.HasField("start"):
    result.append("{}.start missing".format(path))
  else:
    start = source_location.start
  if not source_location.HasField("end"):
    result.append("{}.end missing".format(path))
  else:
    end = source_location.end

  if start and end:
    if start.HasField("line") and end.HasField("line"):
      if start.line > end.line:
        result.append("{}.start.line > {}.end.line ({} vs {})".format(
            path, path, start.line, end.line))
      elif start.line == end.line:
        if (start.HasField("column") and end.HasField("column") and
            start.column > end.column):
          result.append("{}.start.column > {}.end.column ({} vs {})".format(
              path, path, start.column, end.column))

  for name, field in (("start", start), ("end", end)):
    if not field:
      continue
    if field.HasField("line"):
      if field.line <= 0:
        result.append("{}.{}.line <= 0 ({})".format(path, name, field.line))
    else:
      result.append("{}.{}.line missing".format(path, name))
    if field.HasField("column"):
      if field.column <= 0:
        result.append("{}.{}.column <= 0 ({})".format(path, name, field.column))
    else:
      result.append("{}.{}.column missing".format(path, name))

  if min_start and start:
    if min_start.line > start.line or (
        min_start.line == start.line and min_start.column > start.column):
      result.append("{}.start before parent start".format(path))

  if max_end and end:
    if max_end.line < end.line or (
        max_end.line == end.line and max_end.column < end.column):
      result.append("{}.end after parent end".format(path))

  return result


def _check_all_source_locations(proto, path="", min_start=None, max_end=None):
  """Performs sanity checks on all source_locations in proto.

  Arguments:
    proto: The proto to recursively check.
    path: The path, to use in error messages.
    min_start: A minimum value for source_location.start, or None.
    max_end: A maximum value for source_location.end, or None.

  Returns:
    A list of error messages, or an empty list if no errors.
  """
  if path:
    path += "."

  errors = []

  child_start = None
  child_end = None
  # Only check the source_location value if this proto message actually has a
  # source_location field.
  if proto.HasField("source_location"):
    errors.extend(_check_source_location(proto.source_location,
                                         path + "source_location",
                                         min_start, max_end))
    child_start = proto.source_location.start
    child_end = proto.source_location.end

  for name, spec in ir_data_fields.field_specs(proto).items():
    if name == "source_location":
      continue
    if not proto.HasField(name):
      continue
    field_path = "{}{}".format(path, name)
    if spec.is_dataclass:
      if spec.is_sequence:
        index = 0
        for i in getattr(proto, name):
          item_path = "{}[{}]".format(field_path, index)
          index += 1
          errors.extend(
              _check_all_source_locations(i, item_path, child_start, child_end))
      else:
        errors.extend(_check_all_source_locations(getattr(proto, name),
                                                  field_path, child_start,
                                                  child_end))

  return errors


class ModuleIrTest(unittest.TestCase):
  """Tests the module_ir.build_ir() function."""

  def test_build_ir(self):
    ir = module_ir.build_ir(_MINIMAL_SAMPLE)
    ir.source_text = _MINIMAL_SOURCE
    self.assertEqual(ir, _MINIMAL_SAMPLE_IR)

  def test_production_coverage(self):
    """Checks that all grammar productions are used somewhere in tests."""
    used_productions = set()
    module_ir.build_ir(_MINIMAL_SAMPLE, used_productions)
    for test in _get_test_cases():
      module_ir.build_ir(test.parse_tree, used_productions)
    self.assertEqual(set(module_ir.PRODUCTIONS) - used_productions, set([]))

  def test_double_negative_non_compilation(self):
    """Checks that unparenthesized double unary minus/plus is a parse error."""
    for example in ("[x: - -3]", "[x: + -3]", "[x: - +3]", "[x: + +3]"):
      parse_result = parser.parse_module(tokenizer.tokenize(example, "")[0])
      self.assertTrue(parse_result.error)
      self.assertEqual(7, parse_result.error.token.source_location.start.column)
    for example in ("[x:-(-3)]", "[x:+(-3)]", "[x:-(+3)]", "[x:+(+3)]"):
      parse_result = parser.parse_module(tokenizer.tokenize(example, "")[0])
      self.assertFalse(parse_result.error)


def _make_superset_tests():

  def _make_superset_test(test):

    def test_case(self):
      ir = module_ir.build_ir(test.parse_tree)
      is_superset, error_message = test_util.proto_is_superset(ir, test.ir)

      self.assertTrue(
          is_superset,
          error_message + "\n" + ir_data_utils.IrDataSerializer(ir).to_json(indent=2) + "\n" +
          ir_data_utils.IrDataSerializer(test.ir).to_json(indent=2))

    return test_case

  for test in _get_test_cases():
    test_name = "test " + test.name + " proto superset"
    assert not hasattr(ModuleIrTest, test_name)
    setattr(ModuleIrTest, test_name, _make_superset_test(test))


def _make_source_location_tests():

  def _make_source_location_test(test):

    def test_case(self):
      error_list = _check_all_source_locations(
          module_ir.build_ir(test.parse_tree))
      self.assertFalse(error_list, "\n".join([test.name] + error_list))

    return test_case

  for test in _get_test_cases():
    test_name = "test " + test.name + " source location"
    assert not hasattr(ModuleIrTest, test_name)
    setattr(ModuleIrTest, test_name, _make_source_location_test(test))


def _make_negative_tests():

  def _make_negative_test(test):

    def test_case(self):
      parse_result = parser.parse_module(tokenizer.tokenize(test.text, "")[0])
      self.assertEqual(test.error_token, parse_result.error.token.text.strip())

    return test_case

  for test in _get_negative_test_cases():
    test_name = "test " + test.name + " compilation failure"
    assert not hasattr(ModuleIrTest, test_name)
    setattr(ModuleIrTest, test_name, _make_negative_test(test))

_make_negative_tests()
_make_superset_tests()
_make_source_location_tests()


if __name__ == "__main__":
  unittest.main()
