1//! AST for the j language (§3.4).
6#[derive(Debug, Clone, PartialEq)]
8 Con(String), // type name or variable
10 Record(Vec<(String, Rc<TypeExpr>)>),
11 Fun(Rc<TypeExpr>, Rc<TypeExpr>),
14#[derive(Debug, Clone)]
20#[derive(Debug, Clone)]
26 IdLit(String), // unresolved prefix text; resolved to Expr::Id before eval
27 Id(String), // resolved full id
32 Lambda(Vec<Pattern>, Rc<Expr>, Source), // params, body, source text
33 If(Rc<Expr>, Rc<Expr>, Rc<Expr>),
34 Let(Vec<(String, Rc<Expr>)>, Rc<Expr>),
35 App(Rc<Expr>, Rc<Expr>),
36 BinOp(&'static str, Rc<Expr>, Rc<Expr>),
37 Or(Rc<Expr>, Rc<Expr>),
38 Select(Rc<Expr>, String),
39 Update(Rc<Expr>, Vec<(String, Rc<Expr>)>),
40 Record(Vec<(String, Rc<Expr>)>),
43 /// an explicitly parenthesised expression (for non-assoc chain checks)
45 Crash, // internal marker if needed
48/// The source text of a lambda or section, exactly as written, which is how
49/// its closure renders (§5.2): a span of the program text it was parsed
50/// from. The lambdas of one program share that text instead of each holding
51/// a copy, so nested lambdas cost a span each, not their text again.
60 /// The bytes `start..end` of `text`, which must fall on char boundaries.
61 pub fn new(text: Rc<str>, start: usize, end: usize) -> Self {
62 Source { text, start, end }
65 pub fn as_str(&self) -> &str {
66 &self.text[self.start..self.end]
70impl std::fmt::Debug for Source {
71 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72 write!(f, "{:?}", self.as_str())
76#[derive(Debug, Clone)]
78 TypeDecl(String, TypeExpr),
79 Signature(String, TypeExpr, usize), // name, type, line
80 Definition(String, Rc<Expr>, usize),
84 /// Collect free variables of an expression (names not bound within it).
85 pub fn free_vars(&self, bound: &mut Vec<String>, out: &mut std::collections::BTreeSet<String>) {
88 if !bound.iter().any(|b| b == n) {
89 out.insert(n.clone());
92 // `%main` is `labelled "main"` (§4.11)
93 Expr::LabelLit(_) => {
94 if !bound.iter().any(|b| b == "labelled") {
95 out.insert("labelled".to_string());
106 | Expr::SelectorFun(_)
108 Expr::Paren(e) => e.free_vars(bound, out),
109 Expr::Lambda(ps, body, _) => {
110 let start = bound.len();
112 if let Pattern::Var(n) = p {
113 bound.push(n.clone());
116 body.free_vars(bound, out);
117 bound.truncate(start);
119 Expr::If(a, b, c) => {
120 a.free_vars(bound, out);
121 b.free_vars(bound, out);
122 c.free_vars(bound, out);
124 Expr::Let(bs, body) => {
125 let start = bound.len();
127 bound.push(n.clone());
130 e.free_vars(bound, out);
132 body.free_vars(bound, out);
133 bound.truncate(start);
136 f.free_vars(bound, out);
137 x.free_vars(bound, out);
139 Expr::BinOp(_, a, b) | Expr::Or(a, b) => {
140 a.free_vars(bound, out);
141 b.free_vars(bound, out);
143 Expr::Select(e, _) => e.free_vars(bound, out),
144 Expr::Update(e, fs) => {
145 e.free_vars(bound, out);
147 v.free_vars(bound, out);
150 Expr::Record(fs) => {
152 v.free_vars(bound, out);
157 e.free_vars(bound, out);
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list