Skip to content

Commit 6851538

Browse files
authored
Merge pull request #438 from Kmeakin/raw-identifiers
Lex raw identifiers
2 parents d8f0112 + 6da6af4 commit 6851538

File tree

7 files changed

+43
-12
lines changed

7 files changed

+43
-12
lines changed

doc/reference.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ Names start with a an ASCII letter (`a`..`z` or `A`..`Z`), and end with a
117117
sequence of ASCII letters (`a`..`z` or `A`..`Z`), numbers (`0`..`9`), or
118118
underscores (`_`).
119119

120+
Names can be prefixed with `r#` to avoid clashing with keywords, for example `r#let`.
121+
120122
During elaboration, names are resolved to variables bound by:
121123

122124
- [let expressions](#let-expressions)

fathom/src/core/pretty.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use std::cell::RefCell;
2929

3030
use crate::core::{Item, Module, Term};
3131
use crate::source::{StringId, StringInterner};
32+
use crate::surface::lexer::is_keyword;
3233

3334
/// Term precedences
3435
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
@@ -55,6 +56,7 @@ impl<'interner, 'arena> Context<'interner> {
5556

5657
fn string_id(&'arena self, name: StringId) -> RcDoc {
5758
match self.interner.borrow().resolve(name) {
59+
Some(name) if is_keyword(name) => RcDoc::text(format!("r#{name}")),
5860
Some(name) => RcDoc::text(name.to_owned()),
5961
None => RcDoc::text("#error"),
6062
}

fathom/src/surface.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ lalrpop_mod!(
1717
grammar,
1818
"/surface/grammar.rs"
1919
);
20-
mod lexer;
20+
pub mod lexer;
2121
pub mod pretty;
2222

2323
pub mod distillation;

fathom/src/surface/lexer.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,21 @@ use crate::{
66
source::{BytePos, ByteRange},
77
};
88

9+
pub const KEYWORDS: &[&str] = &[
10+
"def", "else", "false", "fun", "if", "let", "match", "overlap", "then", "true", "Type", "where",
11+
];
12+
13+
pub fn is_keyword(word: &str) -> bool {
14+
KEYWORDS.iter().any(|keyword| word == *keyword)
15+
}
16+
917
#[derive(Clone, Debug, Logos)]
1018
pub enum Token<'source> {
1119
#[regex(r"[a-zA-Z_][a-zA-Z0-9_]*")]
20+
#[regex(r"r#[a-zA-Z_][a-zA-Z0-9_]*", |lex| &lex.slice()[2..])]
1221
Name(&'source str),
1322
#[regex(r"\?[a-zA-Z_][a-zA-Z0-9_]*", |lex| &lex.slice()[1..])]
23+
#[regex(r"\?r#[a-zA-Z_][a-zA-Z0-9_]*", |lex| &lex.slice()[3..])]
1424
Hole(&'source str),
1525
#[regex(r#""([^"\\]|\\.)*""#, |lex| &lex.slice()[1..(lex.slice().len() - 1)])]
1626
StringLiteral(&'source str),

fathom/src/surface/pretty.rs

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ use std::cell::RefCell;
55
use crate::source::{StringId, StringInterner};
66
use crate::surface::{BinOp, FormatField, Item, Module, Pattern, Term};
77

8+
use super::lexer::is_keyword;
9+
810
/// Term precedences
911
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
1012
enum Prec {
@@ -42,6 +44,14 @@ impl<'interner, 'arena> Context<'interner, 'arena> {
4244
}
4345
}
4446

47+
fn ident(&'arena self, name: StringId) -> DocBuilder<'arena, Self> {
48+
match self.interner.borrow().resolve(name) {
49+
Some(name) if is_keyword(name) => self.text(format!("r#{name}")),
50+
Some(name) => self.text(name.to_owned()),
51+
None => self.text("#error"),
52+
}
53+
}
54+
4555
pub fn module<Range>(&'arena self, module: &Module<'_, Range>) -> DocBuilder<'arena, Self> {
4656
self.intersperse(
4757
module.items.iter().map(|item| self.item(item)),
@@ -57,13 +67,13 @@ impl<'interner, 'arena> Context<'interner, 'arena> {
5767
self.space(),
5868
match item.r#type {
5969
None => self.concat([
60-
self.string_id(item.label.1),
70+
self.ident(item.label.1),
6171
self.ann_patterns(item.patterns),
6272
self.space(),
6373
]),
6474
Some(r#type) => self.concat([
6575
self.concat([
66-
self.string_id(item.label.1),
76+
self.ident(item.label.1),
6777
self.ann_patterns(item.patterns),
6878
self.space(),
6979
self.text(":"),
@@ -87,7 +97,7 @@ impl<'interner, 'arena> Context<'interner, 'arena> {
8797
fn pattern<Range>(&'arena self, pattern: &Pattern<Range>) -> DocBuilder<'arena, Self> {
8898
match pattern {
8999
Pattern::Placeholder(_) => self.text("_"),
90-
Pattern::Name(_, name) => self.string_id(*name),
100+
Pattern::Name(_, name) => self.ident(*name),
91101
Pattern::StringLiteral(_, number) => self.string_id(*number),
92102
Pattern::NumberLiteral(_, number) => self.string_id(*number),
93103
Pattern::BooleanLiteral(_, boolean) => match *boolean {
@@ -141,8 +151,8 @@ impl<'interner, 'arena> Context<'interner, 'arena> {
141151
// FIXME: indentation and grouping
142152

143153
match term {
144-
Term::Name(_, name) => self.string_id(*name),
145-
Term::Hole(_, name) => self.concat([self.text("?"), self.string_id(*name)]),
154+
Term::Name(_, name) => self.ident(*name),
155+
Term::Hole(_, name) => self.concat([self.text("?"), self.ident(*name)]),
146156
Term::Placeholder(_) => self.text("_"),
147157
Term::Ann(_, expr, r#type) => self.paren(
148158
prec > Prec::Top,
@@ -265,7 +275,7 @@ impl<'interner, 'arena> Context<'interner, 'arena> {
265275
self.text("{"),
266276
type_fields.iter().map(|field| {
267277
self.concat([
268-
self.string_id(field.label.1),
278+
self.ident(field.label.1),
269279
self.space(),
270280
self.text(":"),
271281
self.space(),
@@ -280,7 +290,7 @@ impl<'interner, 'arena> Context<'interner, 'arena> {
280290
self.text("{"),
281291
expr_fields.iter().map(|field| {
282292
self.concat([
283-
self.string_id(field.label.1),
293+
self.ident(field.label.1),
284294
self.space(),
285295
self.text("="),
286296
self.space(),
@@ -306,7 +316,7 @@ impl<'interner, 'arena> Context<'interner, 'arena> {
306316
Term::Proj(_, head_expr, labels) => self.concat([
307317
self.term_prec(Prec::Atomic, head_expr),
308318
self.concat(
309-
(labels.iter()).map(|(_, label)| self.text(".").append(self.string_id(*label))),
319+
(labels.iter()).map(|(_, label)| self.text(".").append(self.ident(*label))),
310320
),
311321
]),
312322
Term::ArrayLiteral(_, exprs) => self.sequence(
@@ -334,7 +344,7 @@ impl<'interner, 'arena> Context<'interner, 'arena> {
334344
Term::FormatCond(_, (_, label), format, cond) => self.concat([
335345
self.text("{"),
336346
self.space(),
337-
self.string_id(*label),
347+
self.ident(*label),
338348
self.space(),
339349
self.text("<-"),
340350
self.space(),
@@ -377,7 +387,7 @@ impl<'interner, 'arena> Context<'interner, 'arena> {
377387
format,
378388
pred,
379389
} => self.concat([
380-
self.string_id(label.1),
390+
self.ident(label.1),
381391
self.space(),
382392
self.text("<-"),
383393
self.space(),
@@ -399,7 +409,7 @@ impl<'interner, 'arena> Context<'interner, 'arena> {
399409
} => self.concat([
400410
self.text("let"),
401411
self.space(),
402-
self.string_id(label.1),
412+
self.ident(label.1),
403413
match r#type {
404414
Some(r#type) => self.concat([
405415
self.space(),
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
let r#def = false;
2+
let r#foo = r#def;
3+
foo

tests/succeed/raw-identifiers.snap

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
stdout = '''
2+
let r#def : Bool = false; let foo : Bool = r#def; foo : Bool
3+
'''
4+
stderr = ''

0 commit comments

Comments
 (0)