1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
// Copyright 2020-2021, The Tremor Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::ast::optimizer::Optimizer;
pub use crate::interpreter::AggrType;
use crate::{
arena::{self, Arena},
ast::{
docs::Docs,
warning::{Warning, Warnings},
Helper,
},
ctx::EventContext,
errors::Result,
highlighter::Highlighter,
lexer::{self, Lexer},
parser::g as grammar,
registry::{Aggr as AggrRegistry, Registry},
Value,
};
use halfbrown::HashMap;
use serde::Serialize;
use std::io;
/// Return of a script execution
#[derive(Debug, Serialize, PartialEq, Eq)]
pub enum Return<'event> {
/// This script should emit the returned
/// value
Emit {
/// Value to emit
value: Value<'event>,
/// Port to emit to
port: Option<String>,
},
/// This event should be dropped
Drop,
/// This script should emit the event that
/// was passed in
EmitEvent {
/// Port to emit to
port: Option<String>,
},
}
/// A tremor script
#[derive(Debug)]
pub struct Script {
/// Script to be executed upon events entering via `in` port
pub script: crate::ast::Script<'static>,
/// A map from input port to runnable script
pub named: HashMap<String, crate::ast::Script<'static>>,
/// Arena index of the string
pub aid: crate::arena::Index,
/// A set of warnings if any
pub warnings: Warnings,
}
impl Script {
/// Removes a deploy from the arena, freeing the memory and marking it valid for reause
/// this function generally should not ever be used. It is a special case for the language
/// server where we know that we really only parse the script to check for errors and
/// warnings.
/// That's also why it's behind a feature falg
#[cfg(feature = "arena-delete")]
pub unsafe fn consume_and_free(self) -> Result<()> {
let Script { aid, script, .. } = self;
drop(script);
Arena::delte_index_this_is_really_unsafe_dont_use_it(aid)
}
/// Get script warnings
pub fn warnings(&self) -> impl Iterator<Item = &Warning> {
self.warnings.iter()
}
/// Parses a string and turns it into a script with the supplied parameters/arguments
///
/// this is used in the language server to delete lements on a
/// parsing error
///
/// # Errors
/// if the script can not be parsed
#[cfg(feature = "arena-delete")]
pub fn parse_with_aid<S>(
src: &S,
reg: &Registry,
) -> std::result::Result<Self, crate::errors::ErrorWithIndex>
where
S: ToString + ?Sized,
{
let (aid, src) = Arena::insert(src)?;
Self::parse_(aid, src, reg).map_err(|e| crate::errors::ErrorWithIndex(aid, e))
}
/// Parses a string and turns it into a script with the supplied parameters/arguments
///
/// # Errors
/// if the script can not be parsed
pub fn parse<S>(src: &S, reg: &Registry) -> Result<Self>
where
S: ToString + ?Sized,
{
let (aid, src) = Arena::insert(src)?;
Self::parse_(aid, src, reg)
}
/// Parses a string and turns it into a script with the supplied parameters/arguments
///
/// # Errors
/// if the script can not be parsed
pub(crate) fn parse_(aid: arena::Index, src: &'static str, reg: &Registry) -> Result<Self> {
let tokens = Lexer::new(src, aid).collect::<Result<Vec<_>>>()?;
let filtered_tokens = tokens.into_iter().filter(|t| !t.value.is_ignorable());
let script_raw = grammar::ScriptParser::new().parse(filtered_tokens)?;
let fake_aggr_reg = AggrRegistry::default();
let mut helper = Helper::new(reg, &fake_aggr_reg);
// helper.consts.args = args.clone_static();
let mut script = script_raw.up_script(&mut helper)?;
Optimizer::new(&helper).walk_script(&mut script)?;
let script = script;
Ok(Self {
script,
named: HashMap::new(),
aid,
warnings: helper.warnings,
})
}
/// Returns the documentation for the script
#[must_use]
pub fn docs(&self) -> &Docs {
&self.script.docs
}
/// Format warnings with the given `Highligher`.
/// # Errors
/// on io errors
pub fn format_warnings_with<H: Highlighter>(&self, h: &mut H) -> io::Result<()> {
for w in self.warnings() {
let tokens: Vec<_> = lexer::Lexer::new(Arena::io_get(self.aid)?, self.aid)
.tokenize_until_err()
.collect();
h.highlight_error(None, &tokens, "", true, Some(w.outer), Some(w.into()))?;
}
h.finalize()
}
/// Runs an event through this script
///
/// # Errors
/// if the script fails to run for the given context, event state and metadata
pub fn run<'run, 'event>(
&self,
context: &'run EventContext,
aggr: AggrType,
event: &'run mut Value<'event>,
state: &'run mut Value<'static>,
meta: &'run mut Value<'event>,
) -> Result<Return<'event>>
where
'event: 'run,
{
self.script.run(context, aggr, event, state, meta)
}
}