Skip to documentation
SLOP

tiny.chant.parse.expression

Reference tiny.chant parse expression

Defined in parse.

API (4)

Actions

Public operations.

No direct callersNo direct callsparseexpression
Static calls · unresolved targets: unknown · external targets: unknown.

Source

Called byCallsparse.assertionconsumeprivate sourcelib.chant.src.parse.constanthasScalarConstantShapeprivate sourcelib.chant.src.parse.constantisScalarprivate sourcelib.chant.src.parse.constexprvalidateObjecttest sourcelib.chant.src.parse.expressiontest: integer constant expressions ev...+4 moreparse.state.objectconstantValueparse.expressionevaluateIntegerConstant
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsparse.autoresolveprivate sourcelib.chant.src.parse.constantisScalarprivate sourcelib.chant.src.parse.constexprvalidateObjecttest sourcelib.chant.src.parse.expressiontest: expression types infer from par...test sourcelib.chant.src.parse.expressiontest: pointer inference reuses one ch...+2 moreprivate sourcelib.chant.src.parse.expressionmakePointerTypeparse.state.objectlookupparse.expressioninferType
Static calls · unresolved targets: 0 · external targets: 5.
Called byCallsparse.expressionparseExpressionprivate sourcelib.chant.src.parse.expressionparsePrimaryparse.initializerparseprivate sourcelib.chant.src.parse.initializerparseDesignatorprivate sourcelib.chant.src.parse.type.specparseEnumeratorsprivate sourcelib.chant.src.parse.expressionparseConditionalparse.state.cursoradvanceparse.state.cursorpeekparse.state.memorycreateparse.expressionparseAssignment
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsparse.assertionconsumetest sourcelib.chant.src.parse.autotest: auto resolves simple and pointe...private sourcelib.chant.src.parse.expressionparseConditionalprivate sourcelib.chant.src.parse.expressionparsePostfixChainprivate sourcelib.chant.src.parse.expressionparsePrimary+12 moreparse.expressionparseAssignmentparse.expressionparseExpression
Static calls · unresolved targets: 0 · external targets: 0.

Source: lib/chant/src/parse/expression.zig

zig
const std = @import("std");const chant = @import("../root.zig");const ast = @import("../ast/root.zig");const ctype = @import("type/root.zig");const state = @import("state/root.zig");const cursor = state.cursor;const diagnostic = state.diagnostic;const memory = state.memory;const object = state.object;const start_mod = state.start;const Error = @import("error.zig").Error;const Parser = state.Parser;const token = chant.token;const Kind = token.Kind;pub fn parseExpression(parser: *Parser) Error!*ast.Expr {    return parseAssignment(parser);}pub fn parseAssignment(parser: *Parser) Error!*ast.Expr {    const target = try parseConditional(parser);    const op: ?ast.expr.BinaryOp = switch (cursor.peek(parser).kind) {        .assign => null,        .plus_assign => .add,        .minus_assign => .sub,        .star_assign => .mul,        .slash_assign => .div,        .percent_assign => .rem,        .amp_assign => .bit_and,        .pipe_assign => .bit_or,        .caret_assign => .bit_xor,        .shl_assign => .shl,        .shr_assign => .shr,        else => return target,    };    _ = cursor.advance(parser);    const value = try parseAssignment(parser);    return memory.create(parser, ast.Expr, .{ .assign = .{ .op = op, .target = target, .value = value } });}fn parseConditional(parser: *Parser) Error!*ast.Expr {    const condition = try parseBinary(parser, 0);    if (!cursor.consume(parser, .question)) return condition;    const then_value = try parseExpression(parser);    _ = try cursor.expect(parser, .colon);    const else_value = try parseConditional(parser);    return memory.create(parser, ast.Expr, .{ .conditional = .{        .condition = condition,        .then_value = then_value,        .else_value = else_value,    } });}const Level = struct {    kind: Kind,    op: ast.expr.BinaryOp,    precedence: u8,};const levels = [_]Level{    .{ .kind = .pipe_pipe, .op = .logical_or, .precedence = 1 },    .{ .kind = .amp_amp, .op = .logical_and, .precedence = 2 },    .{ .kind = .pipe, .op = .bit_or, .precedence = 3 },    .{ .kind = .caret, .op = .bit_xor, .precedence = 4 },    .{ .kind = .amp, .op = .bit_and, .precedence = 5 },    .{ .kind = .eq, .op = .eq, .precedence = 6 },    .{ .kind = .ne, .op = .ne, .precedence = 6 },    .{ .kind = .lt, .op = .lt, .precedence = 7 },    .{ .kind = .gt, .op = .gt, .precedence = 7 },    .{ .kind = .le, .op = .le, .precedence = 7 },    .{ .kind = .ge, .op = .ge, .precedence = 7 },    .{ .kind = .shl, .op = .shl, .precedence = 8 },    .{ .kind = .shr, .op = .shr, .precedence = 8 },    .{ .kind = .plus, .op = .add, .precedence = 9 },    .{ .kind = .minus, .op = .sub, .precedence = 9 },    .{ .kind = .star, .op = .mul, .precedence = 10 },    .{ .kind = .slash, .op = .div, .precedence = 10 },    .{ .kind = .percent, .op = .rem, .precedence = 10 },};fn binaryLevel(kind: Kind) ?Level {    for (levels) |level| {        if (level.kind == kind) return level;    }    return null;}fn parseBinary(parser: *Parser, min_precedence: u8) Error!*ast.Expr {    var lhs = try parseUnary(parser);    while (true) {        const level = binaryLevel(cursor.peek(parser).kind) orelse break;        if (level.precedence < min_precedence) break;        _ = cursor.advance(parser);        const rhs = try parseBinary(parser, level.precedence + 1);        lhs = try memory.create(parser, ast.Expr, .{ .binary = .{ .op = level.op, .lhs = lhs, .rhs = rhs } });    }    return lhs;}fn parseUnary(parser: *Parser) Error!*ast.Expr {    switch (cursor.peek(parser).kind) {        .plus => {            _ = cursor.advance(parser);            return parseUnary(parser);        },        .minus => {            _ = cursor.advance(parser);            const operand = try parseUnary(parser);            return memory.create(parser, ast.Expr, .{ .unary = .{ .op = .negate, .operand = operand } });        },        .bang => {            _ = cursor.advance(parser);            const operand = try parseUnary(parser);            return memory.create(parser, ast.Expr, .{ .unary = .{ .op = .logical_not, .operand = operand } });        },        .tilde => {            _ = cursor.advance(parser);            const operand = try parseUnary(parser);            return memory.create(parser, ast.Expr, .{ .unary = .{ .op = .bit_not, .operand = operand } });        },        .star => {            _ = cursor.advance(parser);            const operand = try parseUnary(parser);            return memory.create(parser, ast.Expr, .{ .unary = .{ .op = .deref, .operand = operand } });        },        .amp => {            const amp_index = parser.index;            _ = cursor.advance(parser);            const operand = try parseUnary(parser);            return memory.createExprAt(                parser,                .{ .unary = .{ .op = .address_of, .operand = operand } },                amp_index,            );        },        .plus_plus => {            _ = cursor.advance(parser);            return desugarIncrement(parser, try parseUnary(parser), .add);        },        .minus_minus => {            _ = cursor.advance(parser);            return desugarIncrement(parser, try parseUnary(parser), .sub);        },        .kw_sizeof => {            _ = cursor.advance(parser);            return parseSizeof(parser);        },        .kw_alignof => {            _ = cursor.advance(parser);            return parseAlignof(parser);        },        .lparen => {            if (start_mod.typename(parser, 1)) {                _ = cursor.advance(parser);                const specifiers = try ctype.parseSpecifiers(parser);                if (specifiers.requires_standalone_declaration) {                    return diagnostic.fail(parser, error.UnsupportedConstruct, "fixed enum forward declaration must be standalone");                }                const declarator = try ctype.parseDeclarator(parser, specifiers.type, parseAssignment);                if (declarator.name != null) {                    return diagnostic.fail(parser, error.UnexpectedToken, "cast names a declarator");                }                _ = try cursor.expect(parser, .rparen);                const operand = try parseUnary(parser);                return memory.create(parser, ast.Expr, .{ .cast = .{ .target = declarator.type, .operand = operand } });            }            return parsePostfixChain(parser, try parsePrimary(parser));        },        else => return parsePostfixChain(parser, try parsePrimary(parser)),    }}fn desugarIncrement(parser: *Parser, target: *ast.Expr, op: ast.expr.BinaryOp) Error!*ast.Expr {    const one = try memory.create(parser, ast.Expr, .{ .integer_literal = .{ .value = 1, .type = &ast.types.int_type } });    return memory.create(parser, ast.Expr, .{ .assign = .{ .op = op, .target = target, .value = one } });}fn integerConstantExpression(parser: *Parser, value: i128, c_type: *const ast.Type) Error!*ast.Expr {    if (value >= 0) {        const unsigned: u64 = std.math.cast(u64, value) orelse return diagnostic.fail(parser, error.InvalidConstant, "integer constant is too large");        return memory.create(parser, ast.Expr, .{ .integer_literal = .{ .value = unsigned, .type = c_type } });    }    if (value == std.math.minInt(i128)) return diagnostic.fail(parser, error.InvalidConstant, "integer constant is too small");    const magnitude: u64 = std.math.cast(u64, -value) orelse return diagnostic.fail(parser, error.InvalidConstant, "integer constant is too small");    const operand = try memory.create(parser, ast.Expr, .{ .integer_literal = .{ .value = magnitude, .type = c_type } });    return memory.create(parser, ast.Expr, .{ .unary = .{ .op = .negate, .operand = operand } });}pub fn evaluateIntegerConstant(parser: *const Parser, expr: *ast.Expr) ?i128 {    switch (expr.*) {        .integer_literal => |literal| return @intCast(literal.value),        .identifier => |identifier| return object.constantValue(parser, identifier.name),        .float_literal, .string_literal, .call, .index, .initializer_list => return null,        .unary => |unary| {            const operand = evaluateIntegerConstant(parser, unary.operand) orelse return null;            return switch (unary.op) {                .negate => -operand,                .logical_not => if (operand == 0) 1 else 0,                .bit_not => ~operand,                .address_of, .deref => null,            };        },        .binary => |binary| {            const lhs = evaluateIntegerConstant(parser, binary.lhs) orelse return null;            const rhs = evaluateIntegerConstant(parser, binary.rhs) orelse return null;            return switch (binary.op) {                .add => lhs + rhs,                .sub => lhs - rhs,                .mul => lhs * rhs,                .div => if (rhs == 0) null else @divTrunc(lhs, rhs),                .rem => if (rhs == 0) null else @rem(lhs, rhs),                .lt => if (lhs < rhs) 1 else 0,                .gt => if (lhs > rhs) 1 else 0,                .le => if (lhs <= rhs) 1 else 0,                .ge => if (lhs >= rhs) 1 else 0,                .eq => if (lhs == rhs) 1 else 0,                .ne => if (lhs != rhs) 1 else 0,                .logical_and => if (lhs != 0 and rhs != 0) 1 else 0,                .logical_or => if (lhs != 0 or rhs != 0) 1 else 0,                .bit_and => lhs & rhs,                .bit_or => lhs | rhs,                .bit_xor => lhs ^ rhs,                .shl => if (rhs < 0 or rhs >= 128) null else lhs << @intCast(rhs),                .shr => if (rhs < 0 or rhs >= 128) null else lhs >> @intCast(rhs),            };        },        .assign => return null,        .conditional => |conditional| {            const condition = evaluateIntegerConstant(parser, conditional.condition) orelse return null;            return evaluateIntegerConstant(parser, if (condition != 0) conditional.then_value else conditional.else_value);        },        .cast => |cast| return evaluateIntegerConstant(parser, cast.operand),    }}pub fn inferType(parser: *Parser, expr: *ast.Expr) Error!?*const ast.Type {    switch (expr.*) {        .integer_literal => |literal| return literal.type,        .float_literal => |literal| return literal.type,        .string_literal => return makePointerType(            parser,            expr,            &ast.types.char_type,        ),        .identifier => |identifier| return state.object.lookup(parser, identifier.name),        .call => |call| {            const callee_type = state.object.lookup(parser, call.callee) orelse return null;            if (callee_type.kind != .function) return null;            return callee_type.child;        },        .index => |index| {            const base = try inferType(parser, index.base) orelse return null;            return ast.types.element(base);        },        .initializer_list => |list| return list.type,        .unary => |unary| {            const operand = try inferType(parser, unary.operand) orelse return null;            return switch (unary.op) {                .negate, .bit_not => if (ast.types.isArithmetic(operand)) operand else null,                .logical_not => &ast.types.int_type,                .address_of => makePointerType(parser, expr, operand),                .deref => ast.types.element(operand),            };        },        .binary => |binary| {            const lhs = try inferType(parser, binary.lhs) orelse return null;            const rhs = try inferType(parser, binary.rhs) orelse return null;            return switch (binary.op) {                .lt, .gt, .le, .ge, .eq, .ne, .logical_and, .logical_or => &ast.types.int_type,                .shl, .shr => if (ast.types.isInteger(lhs) and ast.types.isInteger(rhs)) lhs else null,                .add, .sub, .mul, .div, .rem, .bit_and, .bit_or, .bit_xor => if (ast.types.isArithmetic(lhs) and ast.types.isArithmetic(rhs))                    ast.types.commonArithmetic(lhs, rhs)                else                    null,            };        },        .assign => |assign| return inferType(parser, assign.target),        .conditional => |conditional| {            const then_type = try inferType(parser, conditional.then_value) orelse return null;            const else_type = try inferType(parser, conditional.else_value) orelse return null;            if (ast.types.isArithmetic(then_type) and ast.types.isArithmetic(else_type)) {                return ast.types.commonArithmetic(then_type, else_type);            }            if (then_type == else_type) return then_type;            if (then_type.kind == .nullptr_type and ast.types.isPointerLike(else_type)) return else_type;            if (else_type.kind == .nullptr_type and ast.types.isPointerLike(then_type)) return then_type;            return null;        },        .cast => |cast| return cast.target,    }}fn makePointerType(    parser: *Parser,    expr: *ast.Expr,    child: *const ast.Type,) Error!*const ast.Type {    if (parser.nodes.inferredType(expr)) |cached| return cached;    const token_index = parser.nodes.expressionOrigin(expr);    const inferred = try memory.createType(        parser,        .{ .kind = .pointer, .child = child },        memory.directType(token_index, null),    );    parser.nodes.setInferredType(expr, inferred);    return inferred;}fn parseSizeof(parser: *Parser) Error!*ast.Expr {    if (cursor.peek(parser).kind == .lparen and start_mod.typename(parser, 1)) {        _ = cursor.advance(parser);        const specifiers = try ctype.parseSpecifiers(parser);        if (specifiers.requires_standalone_declaration) {            return diagnostic.fail(parser, error.UnsupportedConstruct, "fixed enum forward declaration must be standalone");        }        const declarator = try ctype.parseDeclarator(parser, specifiers.type, parseAssignment);        _ = try cursor.expect(parser, .rparen);        const size = ast.types.byteSize(declarator.type) orelse            return diagnostic.fail(parser, error.UnsupportedConstruct, "sizeof of an incomplete type");        return memory.create(parser, ast.Expr, .{ .integer_literal = .{ .value = size, .type = &ast.types.ulong_type } });    }    const operand = try parseUnary(parser);    _ = operand;    return diagnostic.fail(parser, error.UnsupportedConstruct, "sizeof of an expression");}fn parseAlignof(parser: *Parser) Error!*ast.Expr {    if (cursor.peek(parser).kind == .lparen and start_mod.typename(parser, 1)) {        _ = cursor.advance(parser);        const specifiers = try ctype.parseSpecifiers(parser);        if (specifiers.requires_standalone_declaration) {            return diagnostic.fail(parser, error.UnsupportedConstruct, "fixed enum forward declaration must be standalone");        }        const declarator = try ctype.parseDeclarator(parser, specifiers.type, parseAssignment);        _ = try cursor.expect(parser, .rparen);        const alignment = ast.types.byteAlign(declarator.type) orelse            return diagnostic.fail(parser, error.UnsupportedConstruct, "alignof of an incomplete type");        return memory.create(parser, ast.Expr, .{ .integer_literal = .{ .value = alignment, .type = &ast.types.ulong_type } });    }    const operand = try parseUnary(parser);    _ = operand;    return diagnostic.fail(parser, error.UnsupportedConstruct, "alignof of an expression");}fn parsePostfixChain(parser: *Parser, start: *ast.Expr) Error!*ast.Expr {    var current = start;    while (true) {        switch (cursor.peek(parser).kind) {            .lbracket => {                _ = cursor.advance(parser);                const subscript = try parseExpression(parser);                _ = try cursor.expect(parser, .rbracket);                current = try memory.create(parser, ast.Expr, .{ .index = .{ .base = current, .subscript = subscript } });            },            .plus_plus => {                _ = cursor.advance(parser);                current = try desugarIncrement(parser, current, .add);            },            .minus_minus => {                _ = cursor.advance(parser);                current = try desugarIncrement(parser, current, .sub);            },            else => return current,        }    }}fn parsePrimary(parser: *Parser) Error!*ast.Expr {    switch (cursor.peek(parser).kind) {        .integer => {            const token_index = parser.index;            const tok = cursor.advance(parser);            const decoded = token.decodeInteger(tok.text) orelse                return diagnostic.fail(parser, error.InvalidConstant, "invalid integer constant");            const literal_type: *const ast.Type = if (decoded.bit_width) |width|                try memory.createType(                    parser,                    .{                        .kind = .bitint_type,                        .is_unsigned = decoded.is_unsigned,                        .bit_width = width,                    },                    memory.directType(token_index, null),                )            else if (decoded.is_unsigned)                (if (decoded.is_long) &ast.types.ulong_type else &ast.types.uint_type)            else                (if (decoded.is_long) &ast.types.long_type else &ast.types.int_type);            return memory.create(parser, ast.Expr, .{ .integer_literal = .{ .value = decoded.value, .type = literal_type } });        },        .floating => {            const tok = cursor.advance(parser);            const decoded = token.decodeFloat(parser.arena, tok.text) orelse                return diagnostic.fail(parser, error.InvalidConstant, "invalid float constant");            const literal_type: *const ast.Type = switch (decoded.kind) {                .float => &ast.types.float_type,                .double => &ast.types.double_type,                .decimal32 => &ast.types.decimal32_type,                .decimal64 => &ast.types.decimal64_type,                .decimal128 => &ast.types.decimal128_type,            };            return memory.create(parser, ast.Expr, .{ .float_literal = .{ .value = decoded.value, .type = literal_type } });        },        .kw_true, .kw_false => {            const tok = cursor.advance(parser);            const value: u64 = if (tok.kind == .kw_true) 1 else 0;            return memory.create(parser, ast.Expr, .{ .integer_literal = .{ .value = value, .type = &ast.types.uchar_type } });        },        .kw_nullptr => {            _ = cursor.advance(parser);            return memory.create(parser, ast.Expr, .{ .integer_literal = .{ .value = 0, .type = &ast.types.nullptr_type } });        },        .character => {            const tok = cursor.advance(parser);            const value = token.decodeCharacter(tok.text) orelse                return diagnostic.fail(parser, error.InvalidConstant, "invalid character constant");            return memory.create(parser, ast.Expr, .{ .integer_literal = .{ .value = value, .type = &ast.types.int_type } });        },        .string => {            const token_index = parser.index;            const tok = cursor.advance(parser);            const text = token.decodeString(parser.arena, tok.text) orelse                return diagnostic.fail(parser, error.InvalidConstant, "invalid string constant");            return memory.createExprAt(                parser,                .{ .string_literal = .{ .text = text } },                token_index,            );        },        .identifier => {            const tok = cursor.advance(parser);            if (cursor.peek(parser).kind == .lparen) {                _ = cursor.advance(parser);                var arguments = std.ArrayListUnmanaged(*ast.Expr).empty;                if (!cursor.consume(parser, .rparen)) {                    while (true) {                        try arguments.append(parser.arena, try parseAssignment(parser));                        if (!cursor.consume(parser, .comma)) break;                    }                    _ = try cursor.expect(parser, .rparen);                }                return memory.create(parser, ast.Expr, .{ .call = .{                    .callee = tok.text,                    .arguments = try arguments.toOwnedSlice(parser.arena),                } });            }            if (object.constantValue(parser, tok.text)) |value| {                const constant_type = object.lookup(parser, tok.text) orelse &ast.types.int_type;                return integerConstantExpression(parser, value, constant_type);            }            return memory.create(parser, ast.Expr, .{ .identifier = .{ .name = tok.text } });        },        .lparen => {            _ = cursor.advance(parser);            const inner = try parseExpression(parser);            _ = try cursor.expect(parser, .rparen);            return inner;        },        else => return diagnostic.fail(parser, error.UnexpectedToken, "expected an expression"),    }}fn parseSource(arena: std.mem.Allocator, source: []const u8) !*ast.Expr {    const lexer = @import("../lexer/root.zig");    const token_survey = try lexer.survey(source, "expr.c");    const capacity = try lexer.Capacity.derive(token_survey.limits);    const bytes = try arena.alignedAlloc(        u8,        .fromByteUnits(lexer.Storage.storage_alignment),        capacity.storage_bytes,    );    var storage = try lexer.Storage.init(bytes, token_survey.limits);    storage.activate();    defer _ = storage.deinit();    const tokens = try storage.fill(token_survey, source, "expr.c");    var parser = try @import("state/test.zig").initParser(arena, tokens);    return parseExpression(&parser);}test "precedence binds multiplication over addition" {    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const arena = arena_state.allocator();    const sum = try parseSource(arena, "a + b * c");    try std.testing.expectEqual(ast.expr.BinaryOp.add, sum.binary.op);    try std.testing.expectEqual(ast.expr.BinaryOp.mul, sum.binary.rhs.binary.op);    const comparison = try parseSource(arena, "i * n + j < bound == 0");    try std.testing.expectEqual(ast.expr.BinaryOp.eq, comparison.binary.op);    try std.testing.expectEqual(ast.expr.BinaryOp.lt, comparison.binary.lhs.binary.op);}test "assignments nest right and compound ops decode" {    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const arena = arena_state.allocator();    const chain = try parseSource(arena, "a = b = 1");    try std.testing.expect(chain.assign.op == null);    try std.testing.expect(chain.assign.value.assign.op == null);    const compound = try parseSource(arena, "C[i][j] += alpha * A[i][k]");    try std.testing.expectEqual(ast.expr.BinaryOp.add, compound.assign.op.?);    try std.testing.expect(compound.assign.target.* == .index);    try std.testing.expect(compound.assign.target.index.base.* == .index);}test "unary postfix and casts parse" {    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const arena = arena_state.allocator();    const increment = try parseSource(arena, "i++");    try std.testing.expectEqual(ast.expr.BinaryOp.add, increment.assign.op.?);    const cast = try parseSource(arena, "(double)(i % n) / n");    try std.testing.expectEqual(ast.expr.BinaryOp.div, cast.binary.op);    try std.testing.expect(cast.binary.lhs.* == .cast);    const ternary = try parseSource(arena, "x > 0 ? f(x, 1) : -x");    try std.testing.expect(ternary.* == .conditional);    try std.testing.expectEqual(@as(usize, 2), ternary.conditional.then_value.call.arguments.len);    const size = try parseSource(arena, "sizeof(double)");    try std.testing.expectEqual(@as(u64, 8), size.integer_literal.value);    const alignment = try parseSource(arena, "alignof(double)");    try std.testing.expectEqual(@as(u64, 8), alignment.integer_literal.value);}test "c23 constants parse as existing scalar expressions" {    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const arena = arena_state.allocator();    const binary = try parseSource(arena, "0b1010'0101");    try std.testing.expectEqual(@as(u64, 0xa5), binary.integer_literal.value);    const signed_bitint = try parseSource(arena, "3wb");    try std.testing.expectEqual(ast.types.Kind.bitint_type, signed_bitint.integer_literal.type.kind);    try std.testing.expectEqual(@as(u16, 3), signed_bitint.integer_literal.type.bit_width);    const unsigned_bitint = try parseSource(arena, "3uwb");    try std.testing.expectEqual(ast.types.Kind.bitint_type, unsigned_bitint.integer_literal.type.kind);    try std.testing.expect(unsigned_bitint.integer_literal.type.is_unsigned);    try std.testing.expectEqual(@as(u16, 2), unsigned_bitint.integer_literal.type.bit_width);    const separated = try parseSource(arena, "1.25'5");    try std.testing.expectEqual(@as(f64, 1.255), separated.float_literal.value);    const decimal32 = try parseSource(arena, "1.25df");    try std.testing.expectEqual(ast.types.Kind.decimal32_type, decimal32.float_literal.type.kind);    const decimal64 = try parseSource(arena, "1.25DD");    try std.testing.expectEqual(ast.types.Kind.decimal64_type, decimal64.float_literal.type.kind);    const decimal128 = try parseSource(arena, "1.25DL");    try std.testing.expectEqual(ast.types.Kind.decimal128_type, decimal128.float_literal.type.kind);    const truth = try parseSource(arena, "true ? 299'792'458 : false");    try std.testing.expect(truth.* == .conditional);    try std.testing.expectEqual(@as(u64, 1), truth.conditional.condition.integer_literal.value);    try std.testing.expectEqual(@as(u64, 0), truth.conditional.else_value.integer_literal.value);    const null_expr = try parseSource(arena, "nullptr");    try std.testing.expectEqual(ast.types.Kind.nullptr_type, null_expr.integer_literal.type.kind);}test "integer constant expressions evaluate" {    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const arena = arena_state.allocator();    const expr = try parseSource(arena, "alignof(double) == 8 && (1 + 2 * 3) == 7");    var parser = try @import("state/test.zig").initParser(arena, &.{});    try std.testing.expectEqual(@as(i128, 1), evaluateIntegerConstant(&parser, expr).?);}test "expression types infer from parser objects" {    const lexer = @import("../lexer/root.zig");    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const arena = arena_state.allocator();    const source = "x + 1.0";    const token_survey = try lexer.survey(source, "expr.c");    const capacity = try lexer.Capacity.derive(token_survey.limits);    const bytes = try arena.alignedAlloc(        u8,        .fromByteUnits(lexer.Storage.storage_alignment),        capacity.storage_bytes,    );    var storage = try lexer.Storage.init(bytes, token_survey.limits);    storage.activate();    defer _ = storage.deinit();    const tokens = try storage.fill(token_survey, source, "expr.c");    var parser = try @import("state/test.zig").initParser(arena, tokens);    try state.object.register(&parser, "x", &ast.types.int_type);    const expr = try parseExpression(&parser);    const inferred = (try inferType(&parser, expr)).?;    try std.testing.expectEqual(ast.types.Kind.double_type, inferred.kind);}test "pointer inference reuses one charged type" {    comptime {        @stardustClaim(            @import("alloc_phase").capacity.witness(@import("./state/root.zig").Storage, "chant_parser_type_cache"),            null,            null,            null,            null,            null,            null,        );    }    const lexer = @import("../lexer/root.zig");    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const arena = arena_state.allocator();    const source = "&x";    const token_survey = try lexer.survey(source, "infer.c");    const capacity = try lexer.Capacity.derive(token_survey.limits);    const bytes = try arena.alignedAlloc(        u8,        .fromByteUnits(lexer.Storage.storage_alignment),        capacity.storage_bytes,    );    var storage = try lexer.Storage.init(bytes, token_survey.limits);    storage.activate();    defer _ = storage.deinit();    const tokens = try storage.fill(token_survey, source, "infer.c");    var parser = try @import("state/test.zig").initParser(arena, tokens);    try state.object.register(&parser, "x", &ast.types.int_type);    const expr = try parseExpression(&parser);    const first = (try inferType(&parser, expr)).?;    const after_first = parser.nodes.status();    const second = (try inferType(&parser, expr)).?;    try std.testing.expectEqual(first, second);    try std.testing.expectEqual(after_first, parser.nodes.status());}

Source: lib/chant/src/parse/root.zig:11

zig
pub const expression = @import("expression.zig");

Complete caller list for parse.expression.evaluateIntegerConstant

9 direct callers.

Complete caller list for parse.expression.inferType

7 direct callers.

Complete caller list for parse.expression.parseExpression

17 direct callers.

Audit

Definitions5
Public names6
Members0
Version26.7.0
Revisiondaab053ee433