#semantic #modeling #symbol #macro #data #arena #smodel

nightly macro smodel-proc

Symbol semantic modeling for Rust: procedural macros

12 stable releases

1.0.11 May 6, 2024
1.0.10 May 2, 2024
1.0.8 May 1, 2024
1.0.3 Apr 30, 2024
0.1.0 Apr 29, 2024

#636 in Procedural macros

Download history 956/week @ 2024-04-29 146/week @ 2024-05-06

1,102 downloads per month
Used in 3 crates (via smodel)

Apache-2.0

72KB
1.5K SLoC

SModel

SModel (Semantic Modeling) for Rust provides a friendly way to describe semantic symbols of a language using dynamic dispatches and hierarchy definitions using an arena that allows for circular references.

Definition order

Definition order is sensitive. Define subtypes after their inherited data types while using the struct keyword.

If you define structs in any order, you may get a not found error that terminates the macro.

Example

The basemost data type is the one that comes first. You may name it according to your tastes. You may usually call it symbol or thingy (according to a Microsoft Roslyn's engineer, symbol ought to be called thingy).

use smodel::smodel;

smodel! {
    type Arena = Arena;

    struct Thingy {
        let x: f64 = 0.0;
        let ref y: String = "".into();

        pub fn Thingy() {
            super();
            println!("{}", self.m());
        }

        pub fn m(&self) -> String {
            "".into()
        }

        pub fn m1(&self) {
            println!("base");
        }
    }

    struct Foo: Thingy {
        pub fn Foo() {
            super();
        }

        pub override fn m(&self) -> String {
            "Foo".into()
        }

        pub override fn m1(&self) {
            if true {
                super.m1();
            }
        }
    }
}

fn main() {
    let arena = Arena::new();
    let thingy = Foo::new(&arena);
    println!("{}", thingy.m());
}

Arena

The arena's name is defined as the right-hand side of the first type Arena = ArenaName1; directive.

Fields

A field (a let declaration) has an optional ref modifier indicating whether to use RefCell or Cell. For all, types are either cloned or copied on read. Use ref for heap-allocated resources such as String.

Fields have a pair of a getter (fieldname()) and a setter (set_fieldname(value)).

For mutable hash maps or vectors, it is recommended to use a shared container (see below) that is cloned by reference and not by content.

Fields are always internal to the enclosing module, therefore there are no attributes; the field definition always starts with the let keyword, without a RustDoc comment.

It is recommended for fields to always start with either a underscore _ or a private prefix such as m_, and consequently using accesses such as _x() and set__x(v), or m_x() and set_m_x(), respectively.

Then, you would implement methods that may be overriden by subtypes in a base type, allowing for an unified data type that supports methods that operate on more than one variant.

Shared containers

This crate provides two container data types that are cloned by reference, SharedArray and SharedMap, as well as shared_array! and shared_map! literals.

  • SharedArray is a mutable vector managed by reference counting.
  • SharedMap is a mutable hash map managed by reference counting.

Refer to the crate documentation for usage details.

Constructor

The constructor is a method whose name matches the data type's name. The arena parameter is implicitly prepended to the formal parameter list.

The constructor is translated to a static new method.

The constructor contains a local self variable whose data type is the instance of the enclosing data type.

Subtypes

  • symbol.is::<T>() tests whether symbol is a T subtype.
  • symbol.to::<T>() converts to the T subtype, returning Ok(m) or Err. It may be a contravariant conversion.
  • symbol.into() is a covariant conversion.

Super expression

The super.f() expression is supported by preprocessing the token sequence of a method and transforming it into another Rust code; therefore, it may be used anywhere within an instance method.

super.f() does a lookup in the method lists in the base data types in descending order.

Inheriting documentation

Use the #[inheritdoc] attribute to inherit the RustDoc comment of an overriden method.

#[inheritdoc]
pub override fn m(&self) {
    // Action
}

License

Apache 2.0, copyright © Hydroper

Dependencies

~0.4–0.8MB
~20K SLoC