Skip to content

Commit 94c1205

Browse files
feat(core): create expression references (#2664)
* feat(core): added expression references to Weir * docs(core): add entry to the documentation for the new syntax * feat(core): cover all cases in Weir tests + create MissingDeterminer rule * fix(core): edge cases * fix(core): appease clippy
1 parent 3909d2e commit 94c1205

11 files changed

Lines changed: 292 additions & 66 deletions

File tree

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
// This is a comment with a typo: spelll
2-
// Another line of comment.
2+
// Another line of the same comment.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
|===
2-
| Cell 1 | Cell 2 with typo: errorr
2+
| Cell 1 | Cell 2, but with a typo: errorr
33
|===

harper-comments/tests/language_support_sources/basic.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757

5858
;;;; Frob Grovel
5959

60-
;;; This section of code has some important implications:
60+
;;; This section of the code has some important implications:
6161
;;; 1. Foo.
6262
;;; 2. Bar.
6363
;;; 3. Baz.

harper-comments/tests/language_support_sources/clean.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ impl TestStruct {
1111
/// It has another [link](https://example.com) embedded inside
1212
fn test_function() {}
1313

14-
/// This is some gibberish to try to trigger a lint for sentences that continue for too long
14+
/// This is some gibberish to try to trigger a lint for the sentences that continue for too long
1515
///
16-
/// This is some gibberish to try to trigger a lint for sentences that continue for too long
16+
/// This is some gibberish to try to trigger a lint for the sentences that continue for too long
1717
///
18-
/// This is some gibberish to try to trigger a lint for sentences that continue for too long
18+
/// This is some gibberish to try to trigger a lint for the sentences that continue for too long
1919
///
20-
/// This is some gibberish to try to trigger a lint for sentences that continue for too long
20+
/// This is some gibberish to try to trigger a lint for the sentences that continue for too long
2121
///
22-
/// This is some gibberish to try to trigger a lint for sentences that continue for too long
22+
/// This is some gibberish to try to trigger a lint for the sentences that continue for too long
2323
}
2424

harper-comments/tests/language_support_sources/clean.sol

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ contract TestContract {
1414
*/
1515
function testFunction2(uint256 p) external {}
1616

17-
// This is some gibberish to try to trigger a lint for sentences that continue for too long
17+
// This is some gibberish to try to trigger a lint for the sentences that continue for too long
1818
//
19-
// This is some gibberish to try to trigger a lint for sentences that continue for too long
19+
// This is some gibberish to try to trigger a lint for the sentences that continue for too long
2020
//
21-
// This is some gibberish to try to trigger a lint for sentences that continue for too long
21+
// This is some gibberish to try to trigger a lint for the sentences that continue for too long
2222
//
23-
// This is some gibberish to try to trigger a lint for sentences that continue for too long
23+
// This is some gibberish to try to trigger a lint for the sentences that continue for too long
2424
//
25-
// This is some gibberish to try to trigger a lint for sentences that continue for too long
25+
// This is some gibberish to try to trigger a lint for the sentences that continue for too long
2626
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
expr techNoun [bug, case, change, comment, feature, fix, log, note, problem, reason, report, repro, reproduction, request, response, scenario, screenshot, solution, step, summary, test, ticket, answer, example, explanation, idea, issue, update]
2+
expr requestNounHead @techNoun
3+
expr requestBareNounPhrase [@requestNounHead, (ADJ @requestNounHead), (ADJ ADJ @requestNounHead), (ADV ADJ @requestNounHead), (ADV ADJ ADJ @requestNounHead), (@requestNounHead @requestNounHead), (ADJ @requestNounHead @requestNounHead), (ADV ADJ @requestNounHead @requestNounHead)]
4+
expr narrativeNoun [portrait, nest, glass, hand, coin, toy, victor]
5+
expr narrativeBareNounPhrase [@narrativeNoun, (ADJ @narrativeNoun)]
6+
7+
expr requestMissingDet <([get, provide, give, send, share, attach, include, add, need, want, see, submit, create, report, file, reproduce] @requestBareNounPhrase), ( )>
8+
expr narrativeVerbObjectMissingDet <([painted, built, dropped, raised, found, hid, cheered] @narrativeBareNounPhrase), ( )>
9+
expr narrativePrepObjectMissingDet <([in, on] [studio, tree, floor]), ( )>
10+
11+
expr main [@requestMissingDet, @narrativeVerbObjectMissingDet, @narrativePrepObjectMissingDet]
12+
13+
let message "Add a determiner before this noun phrase."
14+
let description "Detects likely missing determiners in common request phrases and offers to insert one where necessary."
15+
let kind "Grammar"
16+
let becomes [" the ", " a ", " an "]
17+
18+
test "would it be possible to get reproducible example of this?" "would it be possible to get a reproducible example of this?"
19+
test "Would it be possible to get reproducible bug report?" "Would it be possible to get a reproducible bug report?"
20+
test "Please provide detailed reproduction of this issue." "Please provide a detailed reproduction of this issue."
21+
test "Can you send minimal test case?" "Can you send a minimal test case?"
22+
test "We need quick response." "We need a quick response."
23+
test "I can attach short log." "I can attach a short log."
24+
test "Please share minimal repro." "Please share a minimal repro."
25+
test "Could you submit small change?" "Could you submit a small change?"
26+
test "Please provide reproducible example, thanks." "Please provide a reproducible example, thanks."
27+
test "We should create clear summary." "We should create a clear summary."
28+
test "Please provide the report." "Please provide the report."
29+
test "Please provide your report." "Please provide your report."
30+
test "Please provide another report." "Please provide another report."
31+
test "Please provide more detailed report." "Please provide a more detailed report."
32+
test "We can file short ticket." "We can file a short ticket."
33+
test "Could you reproduce minimal scenario?" "Could you reproduce a minimal scenario?"
34+
test "Please send clear explanation." "Please send a clear explanation."
35+
test "We need simple fix." "We need a simple fix."
36+
test "I want quick update." "I want a quick update."
37+
test "Could you share detailed response?" "Could you share a detailed response?"
38+
test "Please attach short screenshot." "Please attach a short screenshot."
39+
test "We should include short note." "We should include a short note."
40+
test "Please give minimal reproduction." "Please give a minimal reproduction."
41+
test "Can you add brief comment?" "Can you add a brief comment?"
42+
test "We want new feature." "We want a new feature."
43+
test "They need clear solution." "They need a clear solution."
44+
45+
allows "Please provide an example of this."
46+
allows "Please provide the example."
47+
allows "Please provide your example."
48+
allows "Please provide another example."
49+
test "We want detailed explanation." "We want a detailed explanation."
50+
test "We need quick answer." "We need a quick answer."
51+
test "Please send clear update." "Please send a clear update."
52+
test "Please provide brief summary." "Please provide a brief summary."
53+
54+
test "The artist painted portrait in studio." "The artist painted a portrait in the studio."
55+
test "The bird built nest in tree." "The bird built a nest in the tree."
56+
test "The child dropped glass on floor." "The child dropped the glass on the floor."
57+
test "The student raised hand quietly." "The student raised a hand quietly."
58+
test "The child found coin outside." "The child found a coin outside."
59+
test "The child hid toy nearby." "The child hid a toy nearby."
60+
test "The crowd cheered victor loudly." "The crowd cheered the victor loudly."
61+
62+
allows "Let's do this for good measure."
63+
allows "This is a test to make sure we don't split up paragraphs on newlines."
64+
allows "This URL is used by the console to properly generate URLs when using the Artisan command line tool."
65+
allows "The timezone is set to \"UTC\" by default as it is suitable for most use cases."
66+
allows "This option can be set to any locale for which you plan to have translation strings."
67+
allows "Use it to show ownership."
68+
allows "This rule attempts to find common errors with redundancy and contractions that may lead to confusion for readers."
69+
allows "ACF is a powerful tool that allows you to add custom fields to your content, providing greater flexibility in how you manage and display information."
70+
allows "Historical records, colonial archives (however problematic their provenance), and oral histories from surviving communities, even if fragmented and distorted, provide crucial data points."
71+
allows "Traditional cartography relies on observable features – mountains, rivers, coastlines – to create representations of space."
72+
allows "My grandfather built timepieces to mark the passage of moments."
73+
allows "I made a note to encourage Eleanor to share more stories with him; reminiscing often proved beneficial for patients struggling with respiratory distress."

harper-core/src/weir/ast.rs

Lines changed: 50 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
use harper_brill::UPOS;
2+
use hashbrown::HashMap;
23
use is_macro::Is;
34
use itertools::Itertools;
45

56
use crate::expr::{Expr, Filter, FirstMatchOf, SequenceExpr, UnlessStep};
67
use crate::patterns::{AnyPattern, DerivedFrom, UPOSSet, WhitespacePattern, Word};
7-
use crate::{CharString, Punctuation, Token};
8+
use crate::{CharString, CharStringExt, Lrc, Punctuation, Token};
9+
10+
use super::Error;
811

912
#[derive(Debug, Clone, Eq, PartialEq)]
1013
pub struct Ast {
@@ -62,6 +65,18 @@ impl Ast {
6265
_ => None,
6366
})
6467
}
68+
69+
/// Iterate through all the expressions in the tree, starting with the one first declared in the
70+
/// tree.
71+
pub fn iter_exprs(&self) -> impl Iterator<Item = (&str, &AstExprNode)> {
72+
self.stmts.iter().filter_map(|stmt| {
73+
if let AstStmtNode::SetExpr { name, value } = stmt {
74+
Some((name.as_str(), value))
75+
} else {
76+
None
77+
}
78+
})
79+
}
6580
}
6681

6782
/// A node that represents an expression that can be used to search through natural language.
@@ -78,53 +93,66 @@ pub enum AstExprNode {
7893
Seq(Vec<AstExprNode>),
7994
Arr(Vec<AstExprNode>),
8095
Filter(Vec<AstExprNode>),
96+
ExprRef(CharString),
8197
Anything,
8298
}
8399

84100
impl AstExprNode {
85101
/// Create an actual expression that fulfills the pattern matching contract defined by this tree.
86-
pub fn to_expr(&self) -> Box<dyn Expr> {
102+
///
103+
/// Requires a map of all expressions currently in the context.
104+
pub fn to_expr(
105+
&self,
106+
ctx_exprs: &HashMap<String, Lrc<Box<dyn Expr>>>,
107+
) -> Result<Box<dyn Expr>, Error> {
87108
match self {
88-
AstExprNode::Anything => Box::new(AnyPattern),
89-
AstExprNode::Progressive => {
90-
Box::new(|tok: &Token, _: &[char]| tok.kind.is_verb_progressive_form())
91-
}
92-
AstExprNode::UPOSSet(upos) => Box::new(UPOSSet::new(upos)),
93-
AstExprNode::Whitespace => Box::new(WhitespacePattern),
94-
AstExprNode::Word(word) => Box::new(Word::from_chars(word)),
95-
AstExprNode::DerivativeOf(word) => Box::new(DerivedFrom::new_from_chars(word)),
96-
AstExprNode::Not(ast_node) => Box::new(UnlessStep::new(
97-
ast_node.to_expr(),
109+
AstExprNode::Anything => Ok(Box::new(AnyPattern)),
110+
AstExprNode::Progressive => Ok(Box::new(|tok: &Token, _: &[char]| {
111+
tok.kind.is_verb_progressive_form()
112+
})),
113+
AstExprNode::UPOSSet(upos) => Ok(Box::new(UPOSSet::new(upos))),
114+
AstExprNode::Whitespace => Ok(Box::new(WhitespacePattern)),
115+
AstExprNode::Word(word) => Ok(Box::new(Word::from_chars(word))),
116+
AstExprNode::DerivativeOf(word) => Ok(Box::new(DerivedFrom::new_from_chars(word))),
117+
AstExprNode::Not(ast_node) => Ok(Box::new(UnlessStep::new(
118+
ast_node.to_expr(ctx_exprs)?,
98119
|_tok: &Token, _: &[char]| true,
99-
)),
120+
)) as Box<dyn Expr>),
100121
AstExprNode::Seq(children) => {
101122
let mut expr = SequenceExpr::default();
102123

103124
for node in children {
104-
expr = expr.then_boxed(node.to_expr());
125+
expr = expr.then_boxed(node.to_expr(ctx_exprs)?);
105126
}
106127

107-
Box::new(expr)
128+
Ok(Box::new(expr))
108129
}
109130
AstExprNode::Arr(children) => {
110131
let mut expr = FirstMatchOf::default();
111132

112133
for node in children {
113-
expr.add_boxed(node.to_expr());
134+
expr.add_boxed(node.to_expr(ctx_exprs)?);
114135
}
115136

116-
Box::new(expr)
117-
}
118-
AstExprNode::Filter(children) => {
119-
Box::new(Filter::new(children.iter().map(|n| n.to_expr()).collect()))
137+
Ok(Box::new(expr))
120138
}
139+
AstExprNode::Filter(children) => Ok(Box::new(Filter::new(
140+
children
141+
.iter()
142+
.map(|n| n.to_expr(ctx_exprs))
143+
.process_results(|iter| iter.collect())?,
144+
))),
121145
AstExprNode::Punctuation(punct) => {
122146
let punct = *punct;
123147

124-
Box::new(move |tok: &Token, _: &[char]| {
148+
Ok(Box::new(move |tok: &Token, _: &[char]| {
125149
tok.kind.as_punctuation().is_some_and(|p| *p == punct)
126-
})
150+
}))
127151
}
152+
AstExprNode::ExprRef(name) => ctx_exprs
153+
.get(&name.to_string())
154+
.map(|e| Box::new(e.clone()) as Box<dyn Expr>)
155+
.ok_or_else(|| Error::UnableToResolveExpr(name.to_string())),
128156
}
129157
}
130158
}

harper-core/src/weir/error.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ pub enum Error {
1010
UnmatchedBrace,
1111
#[error("Expected a comma here.")]
1212
ExpectedComma,
13-
#[error("Expected a valid keyword.")]
13+
#[error("Expected a valid keyword. Got: {0}")]
1414
UnexpectedToken(String),
1515
#[error("Expected a value to be defined.")]
1616
ExpectedVariableUndefined,
@@ -20,4 +20,6 @@ pub enum Error {
2020
InvalidReplacementStrategy,
2121
#[error("Expected a variable type other than the one provided.")]
2222
ExpectedDifferentVariableType,
23+
#[error("Unable to resolve expression reference {0}")]
24+
UnableToResolveExpr(String),
2325
}

0 commit comments

Comments
 (0)