compiler: Publish fully initialized immutable AST nodes

68c2ad5710e00522babd4db2653ae28feb828c2a54fb3504142096a930a02c7c
Alexis Sellier committed ago 1 parent 6f1f12f2
lib/std/lang/ast.rad +2 -2
918 918
        else => return false,
919 919
    }
920 920
}
921 921
922 922
/// Allocate a new AST node from the arena with the given span and value.
923 -
export unsafe fn allocNode(arena: &mut NodeArena, span: Span, value: NodeValue) -> *mut Node {
923 +
export unsafe fn allocNode(arena: &mut NodeArena, span: Span, value: NodeValue) -> *Node {
924 924
    let p = try! alloc::alloc(&mut arena.arena, @sizeOf(Node), @alignOf(Node));
925 925
    let node = p as *mut Node;
926 926
    let nodeId = arena.nextId;
927 927
    set arena.nextId = nodeId + 1;
928 928
930 930
931 931
    return node;
932 932
}
933 933
934 934
/// Allocate a synthetic AST node with a zero-length span.
935 -
export unsafe fn synthNode(arena: &mut NodeArena, value: NodeValue) -> *mut Node {
935 +
export unsafe fn synthNode(arena: &mut NodeArena, value: NodeValue) -> *Node {
936 936
    return allocNode(arena, Span { offset: 0, length: 0 }, value);
937 937
}
938 938
939 939
/// Synthetic module with a single function in it.
940 940
record SynthFnMod: Copy {
lib/std/lang/parser.rad +9 -22
1086 1086
        return expr;
1087 1087
    }
1088 1088
    return branch;
1089 1089
}
1090 1090
1091 -
/// Allocate a new node from the parser's arena.
1092 -
unsafe fn node 'pool (p: &mut Parser 'pool, value: ast::NodeValue) -> *mut ast::Node {
1091 +
/// Allocate a node with its span set from the most recently consumed token.
1092 +
unsafe fn node 'pool (p: &mut Parser 'pool, value: ast::NodeValue) -> *ast::Node {
1093 +
    let start = p.previous.offset;
1094 +
    let end = start + p.previous.source.len;
1093 1095
    let span = ast::Span {
1094 -
        offset: p.previous.offset,
1095 -
        length: p.previous.source.len,
1096 +
        offset: start,
1097 +
        length: end - start if end >= start else 0,
1096 1098
    };
1097 -
    let n = ast::allocNode(p.arena, span, value);
1098 -
    finishSpan(p, n);
1099 -
1100 -
    return n;
1101 -
}
1102 -
1103 -
/// Update the span of `node` using the most recently consumed token.
1104 -
fn finishSpan 'pool (p: &mut Parser 'pool, node: &mut ast::Node) {
1105 -
    let start: u32 = node.span.offset;
1106 -
    let mut end: u32 = p.previous.offset + p.previous.source.len;
1107 -
1108 -
    if end >= start {
1109 -
        set node.span.length = end - start;
1110 -
    } else {
1111 -
        set node.span.length = 0;
1112 -
    }
1099 +
    return ast::allocNode(p.arena, span, value);
1113 1100
}
1114 1101
1115 1102
/// Save parser state for speculative parsing.
1116 1103
fn saveState 'pool (p: &Parser 'pool) -> SavedState {
1117 1104
    return SavedState {
2445 2432
        ident: binding.name, type: binding.type, value, alignment: binding.alignment, mutable,
2446 2433
    }));
2447 2434
}
2448 2435
2449 2436
/// Parse a module from source text using the provided arena for node storage.
2450 -
export unsafe fn parse(sourceLoc: scanner::SourceLoc, input: *[u8], arena: &mut ast::NodeArena, pool: &mut strings::Pool) -> *mut ast::Node
2437 +
export unsafe fn parse(sourceLoc: scanner::SourceLoc, input: *[u8], arena: &mut ast::NodeArena, pool: &mut strings::Pool) -> *ast::Node
2451 2438
    throws (ParseError)
2452 2439
{
2453 2440
    let poolRef: 'pool = &mut *pool, arenaRef = &mut *arena in {
2454 2441
        let mut p = mkParser(sourceLoc, input, arenaRef, poolRef);
2455 2442
        return try parseModule(&mut p) catch {
2461 2448
2462 2449
/// Parse a complete module into a block of top-level statements.
2463 2450
///
2464 2451
/// This is the main entry point for parsing an entire Radiance source file.
2465 2452
/// The parser must already be initialized with source code.
2466 -
export unsafe fn parseModule 'pool (p: &mut Parser 'pool) -> *mut ast::Node
2453 +
export unsafe fn parseModule 'pool (p: &mut Parser 'pool) -> *ast::Node
2467 2454
    throws (ParseError)
2468 2455
{
2469 2456
    advance(p); // Set the parser up with a first token.
2470 2457
2471 2458
    let statements = try parseStmtsUntil(p, scanner::TokenKind::Eof, 512);
lib/std/lang/parser/tests.rad +19 -0
8 8
use std::lang::sexpr;
9 9
use std::lang::ast::printer;
10 10
use std::lang::scanner;
11 11
use std::lang::strings;
12 12
13 +
/// Allocated nodes publish immutable pointers with stable metadata and edges.
14 +
@test unsafe fn testAstNodePublication() throws (testing::TestError) {
15 +
    static STORAGE: [u8; 4096] = [0; 4096];
16 +
    let mut arena = ast::nodeArena(&mut STORAGE[..]);
17 +
    let allocate: unsafe fn(&mut ast::NodeArena, ast::Span, ast::NodeValue) -> *ast::Node = ast::allocNode;
18 +
    let synthesize: unsafe fn(&mut ast::NodeArena, ast::NodeValue) -> *ast::Node = ast::synthNode;
19 +
    let leaf = allocate(&mut arena, ast::Span { offset: 9, length: 4 }, ast::NodeValue::Bool(true));
20 +
    let root = synthesize(&mut arena, ast::NodeValue::ExprStmt(leaf));
21 +
    assert leaf.id == 0;
22 +
    assert leaf.span.offset == 9;
23 +
    assert leaf.span.length == 4;
24 +
    assert root.id == 1;
25 +
    assert root.span.offset == 0;
26 +
    assert root.span.length == 0;
27 +
    let case ast::NodeValue::ExprStmt(child) = root.value else throw testing::TestError::Failed;
28 +
    assert child == leaf;
29 +
    assert arena.nextId == 2;
30 +
}
31 +
13 32
/// Unified arena size.
14 33
constant ARENA_SIZE: u32 = 2097152;
15 34
/// Unified arena storage for all AST allocations.
16 35
static ARENA_STORAGE: [u8; ARENA_SIZE] = [0; ARENA_SIZE];
17 36
/// String pool.