コード例 #1
0
    def test_parse_unknown_function(self):
        source_code = '''\
@start:
    price = BOGUS(flavor)
'''
        with self.assert_raises_compilation_error('reference to unknown function "BOGUS"', line_number=2, char_number=12):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)
コード例 #2
0
    def test_parse_undefined_label(self):
        source_code = '''\
@start:
   => @bogus
'''
        with self.assert_raises_compilation_error('branch to undefined label "@bogus"', line_number=2):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)
コード例 #3
0
    def test_parse_trivial_cycle(self):
        source_code = '''\
@a:
   => @a
'''
        with self.assert_raises_compilation_error('branch to itself in state "@a"', line_number=2):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)
コード例 #4
0
    def test_parse_redeclared_variable(self):
        source_code = '''\
    let flavor = 0
@start:
        '''
        with self.assert_raises_compilation_error('redeclaration of variable "flavor"', line_number=1, char_number=8):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)
コード例 #5
0
    def test_parse_assigned_custom_variable_to_self(self):
        source_code = '''\
    let temp = temp
@start:
        '''
        with self.assert_raises_compilation_error('reference to undeclared variable "temp"', line_number=1, char_number=15):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)
コード例 #6
0
    def test_parse_assignment_to_undeclared_variable(self):
        source_code = '''\
@start:
    bogus = 1
'''
        with self.assert_raises_compilation_error('reference to undeclared variable "bogus"', line_number=2, char_number=4):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)
コード例 #7
0
    def test_parse_late_declaration(self):
        source_code = '''\
@start:
    let temp = 0
        '''
        with self.assert_raises_compilation_error('variables must be declared before the first state definition'):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)
コード例 #8
0
    def test_parse_redeclared_constant(self):
        source_code = '''\
    let WAFFLE = 0
@start:
        '''
        with self.assert_raises_compilation_error('redeclaration of constant "WAFFLE"', line_number=1, char_number=8):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)
コード例 #9
0
    def test_parse_unexpected_text_after_line_continuation(self):
        source_code = '''\
@start:
    price = scoops + \\ !
            sprinkles * 0.50
'''
        with self.assert_raises_compilation_error('unexpected text after line-continuation character', line_number=2, char_number=23):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)
コード例 #10
0
    def test_parse_redeclared_custom_variable(self):
        source_code = '''\
    let temp = 0
    let temp = 1
@start:
        '''
        with self.assert_raises_compilation_error('redeclaration of variable "temp"', line_number=2, char_number=8):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)
コード例 #11
0
    def test_invalid_constant_value(self):
        source_code = '''\
@start:
'''
        with self.assertRaises(ValueError) as raised:
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, dict(ICE_CREAM_CONSTANTS, WAFFLE=None))
        self.assertEqual(
            str(raised.exception),
            'the value of constant "WAFFLE" (None) is not an int, float, or Decimal'
        )
コード例 #12
0
    def test_overlapping_variable_and_constant_names(self):
        source_code = '''\
@start:
'''
        with self.assertRaises(ValueError) as raised:
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, dict(ICE_CREAM_CONSTANTS, scoops=1))
        self.assertEqual(
            str(raised.exception),
            '"scoops" cannot be both a variable and a constant'
        )
コード例 #13
0
    def test_parse_duplicate_label(self):
        source_code = '''\
@a:
   => @b
@b:
   => @c
@a:
   price = 1
'''
        with self.assert_raises_compilation_error('duplicate label "@a"', line_number=5):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)
コード例 #14
0
    def test_parse_wrong_number_of_function_parameters(self):
        source_code = '''\
@start:
    price = MIN(1)
'''
        with self.assert_raises_compilation_error('function MIN() accepts between 2 and 100 parameters (1 provided)', line_number=2, char_number=12):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)

        source_code = '''\
@start:
    price = ABS(-1, 2)
'''
        with self.assert_raises_compilation_error('function ABS() accepts 1 parameter (2 provided)', line_number=2, char_number=12):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)
コード例 #15
0
    def test_parse_illegal_assignment(self):
        source_code = '''\
@start:
    2 = 1
'''
        with self.assert_raises_compilation_error('illegal assignment', line_number=2, char_number=6):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)

        source_code = '''\
@start:
    WAFFLE = 1
'''
        with self.assert_raises_compilation_error('illegal assignment', line_number=2, char_number=11):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)

        source_code = '''\
@start:
    random! = 1
'''
        with self.assert_raises_compilation_error('illegal assignment', line_number=2, char_number=12):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)

        source_code = '''\
@start:
    price = tax = 1
'''
        with self.assert_raises_compilation_error('chained assignment is not allowed - did you mean == instead?', line_number=2, char_number=16):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)
コード例 #16
0
    def test_parse_illegal_branch_condition(self):
        source_code = '''\
@start:
    bogus => @b
'''
        with self.assert_raises_compilation_error('reference to undeclared variable "bogus"', line_number=2, char_number=4):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)

        source_code = '''\
@start:
    flavor || bogus => @b
'''
        with self.assert_raises_compilation_error('reference to undeclared variable "bogus"', line_number=2, char_number=14):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)
コード例 #17
0
    def test_run(self):
        source_code = '''\
@start:
    scoops == 0 => @explode
    price = (scoops * 1.25) + \\  ''' + '''
            ((cone == WAFFLE) * 1.00) + \\
            (sprinkles * 0.25)
    tax = price * 0.10
    total = price + tax
    stampsEarned = scoops > 5 ? 2 * scoops : scoops

@explode:
    price = scoops / 0 # division by zero!

@unused:
    price = 1M # optimized out
'''
        for newline in ['\n', '\r', '\r\n']:
            program, _ = abysmal.compile(source_code.replace('\n', newline), ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)
            machine = program.machine(flavor=1, scoops=1, cone=1, sprinkles=0)

            machine.run()
            self.assertEqual(machine['price'], '1.25')

            machine.reset()
            machine.run()
            self.assertEqual(machine['price'], '1.25')

            machine = program.machine(scoops=2, cone=2, sprinkles=True)
            machine.run()
            self.assertEqual(machine['price'], '3.75')
コード例 #18
0
 def assert_compiles_to(self, source_code, expected_variable_names, expected_constants, expected_instructions):
     program, source_map = abysmal.compile(source_code, ['x', 'y', 'result'], {'TRUE': True, 'FALSE': False, 'PI': Decimal('3.14159'), 'HALF': 0.5})
     actual_variable_names, actual_constants, actual_instructions = program.dsmal.split(';')
     self.assertEqual(actual_variable_names, expected_variable_names, 'variables section does not match')
     self.assertEqual(actual_constants, expected_constants, 'constants section does not match')
     actual_instructions = list(zip(re.findall(r'[A-Z][a-z]\d*', actual_instructions), source_map))
     self.assertEqual(actual_instructions, expected_instructions, 'instructions section does not match')
コード例 #19
0
ファイル: test_coverage.py プロジェクト: zillow/abysmal
    def test_get_uncovered_lines(self):
        compiled_program, source_map = abysmal.compile(ICE_CREAM_SOURCE_CODE,
                                                       ICE_CREAM_VARIABLES,
                                                       ICE_CREAM_CONSTANTS)
        machine = compiled_program.machine(
            sprinkles=False, weekday=ICE_CREAM_CONSTANTS['SATURDAY'])
        machine.random_number_iterator = itertools.cycle(
            [1])  # random! is always 1

        coverage_tuples = [
            machine.reset(flavor=flavor, scoops=scoops,
                          cone=cone).run_with_coverage() for flavor in
            [ICE_CREAM_CONSTANTS['VANILLA'], ICE_CREAM_CONSTANTS['CHOCOLATE']]
            for scoops in [1, 2, 3] for cone in
            [ICE_CREAM_CONSTANTS['SUGAR'], ICE_CREAM_CONSTANTS['WAFFLE']]
        ]
        self.assertEqual(
            abysmal.get_uncovered_lines(source_map, coverage_tuples),
            abysmal.CoverageReport(partially_covered_line_numbers=[19, 20, 23],
                                   uncovered_line_numbers=[27, 28, 31]))

        # No runs means all lines are uncovered.
        self.assertEqual(
            abysmal.get_uncovered_lines(source_map, []),
            abysmal.CoverageReport(partially_covered_line_numbers=[],
                                   uncovered_line_numbers=[
                                       18, 19, 20, 21, 22, 23, 24, 27, 28, 31,
                                       34
                                   ]))
コード例 #20
0
    def test_parse_cycle_detected(self):
        source_code = '''\
@a:
    => @b
    => @c
@b:
    => @d
    => @e
@c:
    => @d
@d:
    => @e
@e:
    => @c
'''
        with self.assert_raises_compilation_error('cycle exists between states "@c", "@e", "@d"'):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)
コード例 #21
0
    def test_compile_fold_constants(self):
        program, _ = abysmal.compile(
            '''\
@start:
    a = 0 || 0 || 1 == 2 || 3 != 3 || 4 < 4 || 5 <= 4 || 6 > 7 || 8 >= 9
    b = 0 || 5 || 0
    c = 5 || 3
    d = 5 && 0
    e = 0 && 5 && 3
    f = 5 && (0 || 3) && 2
    g = 3 + 2
    h = 3 - 2
    i = 3 * 2
    j = 3 / 2
    k = 3 ^ 2
    l = !5
    m = !0
    n = +++5
    o = ---5
    p = ABS(-5)
    q = CEILING(-5.2)
    r = FLOOR(5.2)
    s = MAX(-1, 5, 0)
    t = MIN(-2, 13, 0)
    u = ROUND(5.2)
    v = ROUND(5.8)
    w = 0 ? 3 : 4
    x = 5 ? 3 : 4
    y = 5 in {1, a, 3, b}
    z = 5 not in {1, a, 5, b}
    za = 3 in [0, a]
    zb = -5 in (a, 9)
    zc = 1 in (1, a)
    zd = 1 in (a, 1)
    0 => @deadcode
    1 => @alwaysexecuted

@deadcode:

@alwaysexecuted:
''',
            ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'za', 'zb', 'zc', 'zd'], # pylint: disable=line-too-long
            {}
        )
        self.assertEqual(
            program.dsmal,
            'a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|za|zb|zc|zd;'
            '5|-5|3|6|-2|1.5|4|9;'
            'LzSt0LoSt1LoSt2LzSt3LzSt4LoSt5Lc0St6LoSt7Lc3St8Lc5St9Lc7St10LzSt11LoSt12Lc0St13Lc1St14Lc0St15Lc1St16Lc0St17Lc0St18Lc4St19Lc0St20Lc3St21Lc6St22Lc2St23Lc0CpLv0EqJn60CpLv1EqJn60PpLzJu62PpLoSt24LzSt25Lv0Lc2GeSt26Lc1Lv0GtSt27LzSt28LzSt29Xx' # pylint: disable=line-too-long
        )
コード例 #22
0
    def test_compile_eliminate_constant_declared_variables(self):
        program, _ = abysmal.compile(
            '''\
let t1 = 5
let t2 = t1 * 3
let t3 = t2 + a
@start:
    b = t3
    c = t2 - 9
''',
            ['a', 'b', 'c'],
            {}
        )
        self.assertEqual(program.dsmal, 't3|a|b|c;15|6;Lc0Lv1AdSt0Lv0St2Lc1St3Xx')
コード例 #23
0
 def check(expr, expected_result):
     source_code = '@start:\n    result = ' + expr
     program, _ = abysmal.compile(
         source_code,
         {'zero', 'one', 'tenth', 'result'},
         {
             'TRUE': True,
             'FALSE': False,
             'TWO': 2,
             'PI': Decimal('3.14159'),
         }
     )
     machine = program.machine(zero=0, one=1.0, tenth=Decimal('0.1'))
     try:
         machine.run()
         result = Decimal(machine['result'])
     except abysmal.ExecutionError as ex:
         self.assertEqual(str(ex), expected_result)
     else:
         self.assertEqual(result, expected_result)
コード例 #24
0
    def test_compile_eliminated_unreachable_code(self):
        program, _ = abysmal.compile(
            '''\
@start:
    c = a
    => @a
    c = b
@a:
    0 > 1 => @nope
    a > b => @yup
@nope:
    => @nope2
@yup:
    c = b
@nope2:
    c = 100
    a = 200
@nope3:
    c = 300
''',
            ['a', 'b', 'c'],
            {}
        )
        self.assertEqual(program.dsmal, 'a|b|c;;Lv0St2Lv0Lv1GtJn7XxLv1St2Xx')
コード例 #25
0
 def test_parse_unknown_token(self):
     for source_code in ['$flavor', '$4', '"flavor"']:
         with self.assert_raises_compilation_error('unknown token', line_number=1, char_number=0):
             abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)
コード例 #26
0
    def test_ice_cream_price(self):

        # abysmal implementation

        FLAVOR_CONSTANTS = {
            'VANILLA': 1,
            'CHOCOLATE': 2,
            'STRAWBERRY': 3,
        }
        CONE_CONSTANTS = {
            'SUGAR': 1,
            'WAFFLE': 2,
        }
        WEEKDAY_CONSTANTS = {
            'MONDAY': 1,
            'TUESDAY': 2,
            'WEDNESDAY': 3,
            'THURSDAY': 4,
            'FRIDAY': 5,
            'SATURDAY': 6,
            'SUNDAY': 7,
        }

        SOURCE_CODE = '''\
let TAX_RATE = 5.3%
let WEEKDAY_DISCOUNT = 25%

@start:
    price = scoops * (flavor == STRAWBERRY ? 1.25 : 1)
    price = price + (cone == WAFFLE ? 1.00 : 0.00)
    price = price + (sprinkles * 0.25)
    weekday not in {SATURDAY, SUNDAY} => @apply_weekday_discount
    => @compute_total

@apply_weekday_discount:
    price = price * (1 - WEEKDAY_DISCOUNT)
    => @compute_total

@compute_total:
    price = price * (1 + TAX_RATE)
'''

        program, _ = abysmal.compile(
            SOURCE_CODE, {
                'flavor',
                'scoops',
                'cone',
                'sprinkles',
                'weekday',
                'price',
            }, dict(**FLAVOR_CONSTANTS, **CONE_CONSTANTS, **WEEKDAY_CONSTANTS))
        machine = program.machine(flavor=FLAVOR_CONSTANTS['VANILLA'],
                                  scoops=1,
                                  cone=CONE_CONSTANTS['SUGAR'],
                                  sprinkles=False,
                                  weekday=WEEKDAY_CONSTANTS['MONDAY'])

        # native implementation

        STRAWBERRY_MULTIPLIER = Decimal('1.25')
        WAFFLE_CONE_COST = Decimal('1.00')
        SPRINKLES_COST = Decimal('0.25')
        WEEKDAY_MULTIPLIER = Decimal('0.75')
        TAX_MULTIPLIER = Decimal('1.053')

        def native(flavor, scoops, cone, sprinkles, weekday):
            price = Decimal(scoops)
            if flavor == FLAVOR_CONSTANTS['STRAWBERRY']:
                price *= STRAWBERRY_MULTIPLIER
            if cone == CONE_CONSTANTS['WAFFLE']:
                price += WAFFLE_CONE_COST
            if sprinkles:
                price += SPRINKLES_COST
            if weekday not in (WEEKDAY_CONSTANTS['SATURDAY'],
                               WEEKDAY_CONSTANTS['SUNDAY']):
                price *= WEEKDAY_MULTIPLIER
            price = price * TAX_MULTIPLIER
            return price

        # test cases

        cases = [{
            'flavor': flavor,
            'scoops': scoops,
            'cone': cone,
            'sprinkles': sprinkles,
            'weekday': weekday,
        } for flavor in FLAVOR_CONSTANTS.values() for scoops in (1, 2, 3)
                 for cone in CONE_CONSTANTS.values()
                 for sprinkles in (False, True)
                 for weekday in WEEKDAY_CONSTANTS.values()]

        def run_abysmal():
            for case in cases:
                machine.reset(**case).run()
                _ = Decimal(machine['price'])

        def run_native():
            for case in cases:
                _ = native(**case)

        number = 1000
        runs = number * len(cases)
        abysmal_us = 1000000 * min(
            timeit.repeat(stmt='run_abysmal()',
                          number=number,
                          repeat=5,
                          globals=locals())) / runs
        native_us = 1000000 * min(
            timeit.repeat(
                stmt='run_native()', number=number, repeat=5,
                globals=locals())) / runs
        print('''
ICE CREAM PRICE BENCHMARK RESULTS:
  abysmal : {0:.3f} us/run
  native  : {1:.3f} us/run'''.format(abysmal_us, native_us))
コード例 #27
0
 def test_parse_no_states(self):
     for source_code in ['', ' ', '# nothing', 'let z = 0']:
         with self.assert_raises_compilation_error('no states are defined'):
             abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)
コード例 #28
0
 def test_parse_missing_start_state_label(self):
     for source_code in ['price = 1', '=> @_', 'flavor == 1 => @_']:
         with self.assert_raises_compilation_error('missing start state label'):
             abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)
コード例 #29
0
 def test_parse_line_numbers(self):
     source_code = '#1\r#2\n#3\r\n\r\n\n\r$'
     with self.assert_raises_compilation_error('unknown token', line_number=7, char_number=0):
         abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)
コード例 #30
0
    def test_parse_unexpected_token(self):
        source_code = '''\
@start
'''
        with self.assert_raises_compilation_error('expected : but found end-of-line instead', line_number=1, char_number=6):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)

        source_code = '''\
let
'''
        with self.assert_raises_compilation_error('expected identifier but found end-of-line instead', line_number=1, char_number=3):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)

        source_code = '''\
let temp
'''
        with self.assert_raises_compilation_error('expected = but found end-of-line instead', line_number=1, char_number=8):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)

        source_code = '''\
let temp 3
'''
        with self.assert_raises_compilation_error('expected = but found literal instead', line_number=1, char_number=9):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)

        source_code = '''\
let temp +
'''
        with self.assert_raises_compilation_error('expected = but found + instead', line_number=1, char_number=9):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)

        source_code = '''\
let temp, temp2
'''
        with self.assert_raises_compilation_error('expected = but found , instead', line_number=1, char_number=8):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)

        source_code = '''\
let temp =
'''
        with self.assert_raises_compilation_error('unexpected end-of-line', line_number=1, char_number=10):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)

        source_code = '''\
let temp = price = 3
'''
        with self.assert_raises_compilation_error('chained assignment is not allowed - did you mean == instead?', line_number=1, char_number=17):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)

        source_code = '''\
let temp = 0
@start:
    temp = price = 3
'''
        with self.assert_raises_compilation_error('chained assignment is not allowed - did you mean == instead?', line_number=3, char_number=17):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)

        source_code = '''\
@start:
    => y
'''
        with self.assert_raises_compilation_error('expected label but found identifier instead', line_number=2, char_number=7):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)

        source_code = '''\
@start:
    price = flavor in 3
'''
        with self.assert_raises_compilation_error('expected { or [ or ( but found literal instead', line_number=2, char_number=22):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)

        source_code = '''\
@start:
    price = flavor not
'''
        with self.assert_raises_compilation_error('expected in but found end-of-line instead', line_number=2, char_number=22):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)

        source_code = '''\
@start:
    price = flavor not in 5
'''
        with self.assert_raises_compilation_error('expected { or [ or ( but found literal instead', line_number=2, char_number=26):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)

        source_code = '''\
@start:
    price = @b
'''
        with self.assert_raises_compilation_error('unexpected label', line_number=2, char_number=12):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)

        source_code = '''\
@start:
    flavor || || => @b
'''
        with self.assert_raises_compilation_error('unexpected ||', line_number=2, char_number=14):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)

        source_code = '''\
@start:
    < 3 => @b
'''
        with self.assert_raises_compilation_error('unexpected <', line_number=2, char_number=4):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)

        source_code = '''\
@start:
    price = !
'''
        with self.assert_raises_compilation_error('unexpected end-of-line', line_number=2, char_number=13):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)

        source_code = '''\
@start:
    price = flavor in {}
'''
        with self.assert_raises_compilation_error('unexpected }', line_number=2, char_number=23):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)

        source_code = '''\
@start:
    price = flavor in {3,}
'''
        with self.assert_raises_compilation_error('unexpected }', line_number=2, char_number=25):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)

        source_code = '''\
@start:
    price = scoops in []
'''
        with self.assert_raises_compilation_error('unexpected ]', line_number=2, char_number=23):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)

        source_code = '''\
@start:
    price = scoops in [1]
'''
        with self.assert_raises_compilation_error('expected , but found ] instead', line_number=2, char_number=24):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)

        source_code = '''\
@start:
    price = scoops in [1, ]
'''
        with self.assert_raises_compilation_error('unexpected ]', line_number=2, char_number=26):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)

        source_code = '''\
@start:
    price = scoops in [1, 2, 3]
'''
        with self.assert_raises_compilation_error('expected ] or ) but found , instead', line_number=2, char_number=27):
            abysmal.compile(source_code, ICE_CREAM_VARIABLES, ICE_CREAM_CONSTANTS)