Skip to main content

yog_script_analyze/
words.rs

1//! The words of Yog Script. Each word is one file under `words/`: how it's
2//! written, what it means (shown by editors) and how it parses. The parser
3//! core only hands each word its turn; this file only lists them.
4
5use std::fmt;
6
7use crate::ast::{DeclKind, Expr, Modifier, PatchItem, Point, Stmt};
8use crate::parser::{PResult, Parser};
9
10/// One word of the language.
11pub struct Word {
12    /// How it's written.
13    pub text: &'static str,
14    /// What it means, for editors (hover, completion).
15    pub doc: &'static str,
16    /// Reserved everywhere (a keyword); otherwise a name where its forms
17    /// don't apply.
18    pub reserved: bool,
19    /// What it does where it stands.
20    pub roles: &'static [Role],
21}
22
23/// Whether a form starts here, beyond the word itself being here.
24pub type Starts = Option<fn(&Parser) -> bool>;
25
26/// What a word does where the parser meets it.
27#[derive(Clone, Copy)]
28pub enum Role {
29    /// Starts a declaration.
30    Decl {
31        starts: Starts,
32        parse: fn(&mut Parser) -> PResult<DeclKind>,
33    },
34    /// Starts a statement.
35    Stmt {
36        parse: fn(&mut Parser) -> PResult<Stmt>,
37    },
38    /// Starts an expression (a literal, a prefix, `if`, `match`).
39    Prefix {
40        starts: Starts,
41        parse: fn(&mut Parser) -> PResult<Expr>,
42    },
43    /// Joins the expression before it with what follows; `negated` when
44    /// written `!word` (`!is`, `!in`).
45    Infix {
46        level: Level,
47        parse: fn(&mut Parser, Expr, bool) -> PResult<Expr>,
48        negatable: bool,
49    },
50    /// Marks the declaration after it.
51    Modifier(Modifier),
52    /// Starts an item of a patch (`before`, `add`, …).
53    PatchItem {
54        parse: fn(&mut Parser) -> PResult<PatchItem>,
55    },
56    /// Starts a point inside a patched method (`around`, `at`, …).
57    Point {
58        parse: fn(&mut Parser) -> PResult<Point>,
59    },
60    /// Only part of another word's form (`else`, `was`, `of`).
61    Part,
62}
63
64/// Binding strength of infix forms, loosest first (`ys-grammar.md` §3.2).
65#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
66pub enum Level {
67    Coalesce,
68    Or,
69    And,
70    Compare,
71    Range,
72    Sum,
73    Product,
74    Convert,
75}
76
77impl Word {
78    /// Its role of the kind `pick` finds.
79    pub fn role<T>(&self, pick: impl Fn(&Role) -> Option<T>) -> Option<T> {
80        self.roles.iter().find_map(pick)
81    }
82}
83
84impl PartialEq for Word {
85    fn eq(&self, other: &Word) -> bool {
86        self.text == other.text
87    }
88}
89
90impl fmt::Debug for Word {
91    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92        write!(f, "`{}`", self.text)
93    }
94}
95
96/// The reserved word written `text`.
97pub fn reserved(text: &str) -> Option<&'static Word> {
98    ALL.iter().copied().find(|w| w.reserved && w.text == text)
99}
100
101/// The word written `text`, reserved or not.
102pub fn named(text: &str) -> Option<&'static Word> {
103    ALL.iter().copied().find(|w| w.text == text)
104}
105
106macro_rules! words {
107    ($($file:ident $(= $path:literal)?),* $(,)?) => {
108        $($(#[path = $path])? pub(crate) mod $file;)*
109
110        /// Every word, in the order the parser offers them a turn.
111        pub static ALL: &[&Word] = &[$(&$file::WORD),*];
112    };
113}
114
115words! {
116    // Files and declarations.
117    // `mod.rs` and `self` can't name a module file: theirs say `_word`.
118    r#use, module = "words/mod_word.rs", r#fn, r#let, r#var, val, r#const, r#type, unit, record, union, r#enum,
119    extend, on, test, native, version, recipe, screen, command, state, action, slots,
120    patch,
121    // Parts of declarations.
122    was, requires, ui, of, mc,
123    // Modifiers.
124    server, client, shared, saved, synced, tune, r#pub,
125    // Statements.
126    r#for, r#while, r#loop, r#break, r#continue, r#return, skip,
127    // Expressions.
128    r#if, r#else, r#match, r#try, r#true, r#false, none, this = "words/self_word.rs", it, not, and, or, is, r#as, r#in,
129    // Patches: items, points, selectors.
130    add, before, after, inside, replace, read, write, priority, uses, local,
131    around, at, call, new, constant, first, last, r#where,
132}