// 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::raw::*;
use crate::ast::deploy::raw::*;
use crate::ast::query::raw::*;
use crate::ast::module::*;
use crate::ast::{BinOpKind, UnaryOpKind, NodeId};
use crate::lexer::Token;
use crate::pos::Location;
use crate::Value;
use crate::prelude::*;
use crate::NodeMeta;
use beef::Cow;
grammar<'input>;
////////////////////////////// Module system //////////////////////////////
// Statements or partials shared between grammers
pub(crate) ModuleFile: ModuleRaw<'input> = {
<ModuleBody> => <>,
}
ModuleBody: ModuleRaw<'input> = {
<start:@L> <doc:(ModComment)?> <stmts:ModuleStmts> <end:@L> => ModuleRaw{name: IdentRaw::none(NodeMeta::new_box(start, end)), mid: NodeMeta::new_box(start, end), stmts, doc},
}
ModComment: Vec<Cow<'input, str>> = {
<c:"<mod-comment>"> => vec![c.into()],
<v:ModComment> <c:"<mod-comment>"> => {
let mut v = v;
v.push(c.into());
v
},
}
/// Comments
DocComment: Vec<Cow<'input, str>> = {
<c:"<doc-comment>"> => vec![c.into()],
<v:DocComment> <c:"<doc-comment>"> => {
let mut v = v;
v.push(c.into());
v
},
}
/// List of expressions at the root of the script
ModuleStmts: ModuleStmtsRaw<'input> = {
<stmt:ModuleStmt> ";" <list:ModuleStmts> => {
let mut list = list;
list.insert(0,stmt);
list
},
<item:ModuleStmt> ";"? => vec![item],
}
// In case we need to limit the content of deploy stmt
ModuleStmt: ModuleStmtRaw<'input> = {
// Generic
Use => ModuleStmtRaw::Use(<>),
// Script
Const => ModuleStmtRaw::Const(<>),
FnDefn => ModuleStmtRaw::FnDefn(<>),
Intrinsic => ModuleStmtRaw::FnDefn(<>),
// Trickle
DefineWindow => ModuleStmtRaw::Window(<>),
DefineOperator => ModuleStmtRaw::Operator(<>),
DefineScript => ModuleStmtRaw::Script(<>),
DefinePipeline => ModuleStmtRaw::Pipeline(<>),
/// Troy
DefineConnector => ModuleStmtRaw::Connector(<>),
DefineFlow => ModuleStmtRaw::Flow(<>),
}
ConfigDirectives: ConfigRaw<'input> = {
<directive:ConfigDirective> <directives:ConfigDirectives> => {
let mut directives = directives;
directives.insert(0, directive);
directives
},
<directive:ConfigDirective> => vec![directive]
}
ConfigDirective: (IdentRaw<'input>, ImutExprRaw<'input>) = {
"#!config" <WithExpr> => <>
}
Use: UseRaw = {
<start:@L> "use" <module:ModularTarget> <end:@L> => UseRaw{module, alias: None, mid: NodeMeta::new_box(start, end)},
<start:@L> "use" <module:ModularTarget> "as" <alias:Ident> <end:@L> => UseRaw{module, alias: Some(alias.to_string()), mid: NodeMeta::new_box(start, end)},
}
///
/// args
/// arg1,
/// arg2 = 5
/// with
/// config = {},
/// oops = args.arg1
/// end;
ArgsWithEnd: DefinitionalArgsWithRaw<'input> = {
<start:@L> <args:ArgsClause?> <with:WithEndClause> <end:@L> => {
DefinitionalArgsWithRaw {
mid: NodeMeta::new_box(start, end),
args: args.unwrap_or_default(),
with: with
}
},
<start:@L> => {
DefinitionalArgsWithRaw::none(NodeMeta::new_box(start, start))
}
}
/// args
/// arg1,
/// arg2 = 5
/// # something else
DefinitionArgs: DefinitionalArgsRaw<'input> = {
<start:@L> <args:ArgsClause?> <end:@L> => DefinitionalArgsRaw {
args: args.unwrap_or_default(),
mid: NodeMeta::new_box(start, end)
}
}
ArgsClause: ArgsExprsRaw<'input> = {
"args" <ArgsExprs> => ArgsExprsRaw(<>),
}
ArgsExprs: Vec<(IdentRaw<'input>, Option<ImutExprRaw<'input>>)> = {
<Sep<ArgsExprs, ArgsExpr, ",">> => <>
}
ArgsExpr: (IdentRaw<'input>, Option<ImutExprRaw<'input>>) = {
<name:Ident> "=" <expr:ExprImut> => (name, Some(expr)),
<name:Ident> => (name, None)
}
/// with
/// a = 5,
/// b = args.xxx
/// end
CreationWithEnd: CreationalWithRaw<'input> = {
<WithEndClause> => CreationalWithRaw::from(<>),
<start:@L> => CreationalWithRaw::none(NodeMeta::new_box(start, start))
}
CreationWith: CreationalWithRaw<'input> = {
<WithClause> => CreationalWithRaw::from(<>),
<start:@L> => CreationalWithRaw::none(NodeMeta::new_box(start, start))
}
WithClause: WithExprsRaw<'input> = {
<start:@L> "with" <exprs:WithExprs> <end:@L> => WithExprsRaw{exprs, mid: NodeMeta::new_box(start, end) }
}
WithEndClause: WithExprsRaw<'input> = {
<WithClause> "end" => <>,
}
WithExprs: Vec<(IdentRaw<'input>, ImutExprRaw<'input>)> = {
<Sep<WithExprs, WithExpr, ",">> => <>
}
WithExpr: (IdentRaw<'input>, ImutExprRaw<'input>) = {
<name:Ident> "=" <expr:ExprImut> => (name, expr)
}
/// Module target
ModularTarget: NodeId = {
<Ident> => NodeId::from(<>),
<m:ModPath> "::" <target:Ident> => NodeId{id: target.to_string(), module: m.iter().map(ToString::to_string).collect()},
}
////////////////////////////// Troy statements //////////////////////////////
// These statements for the root of the language.
/// Root
pub(crate) Deploy : DeployRaw<'input> = {
<config:ConfigDirectives> <doc:(ModComment)?> <stmts:DeployStmts> => DeployRaw{config, stmts, doc},
<doc:(ModComment)?> <stmts:DeployStmts>=> DeployRaw{config: vec![], stmts, doc},
}
DeployStmts: DeployStmtsRaw<'input> = {
<stmt:DeployStmt> ";" <list:DeployStmts> => {
let mut list = list;
list.insert(0,stmt);
list
},
<item:DeployStmt> ";"? => vec![item],
}
DeployStmt: DeployStmtRaw<'input> = {
DefineFlow => DeployStmtRaw::FlowDefinition(<>),
DeployFlowStmt => DeployStmtRaw::DeployFlow(<>),
Use => DeployStmtRaw::Use(<>),
}
DeployFlowStmt: DeployFlowRaw<'input> = {
<start:@L> <docs:(DocComment)?> "deploy" "flow" <id:Ident> "from" <target:ModularTarget> <params:CreationWithEnd> <end:@L> => DeployFlowRaw { mid: NodeMeta::new_box(start, end), id, target, params, docs },
<start:@L> <docs:(DocComment)?> "deploy" "flow" <id:Ident> <params:CreationWithEnd> <end:@L> => DeployFlowRaw { mid: NodeMeta::new_box(start, end), id: id.clone(), target: id.into(), params, docs },
}
ConnectorKind: IdentRaw<'input> = {
<Ident> => <>,
}
FlowStmts: FlowStmtsRaw<'input> = {
<fields:FlowStmts_> => {
let mut fields = fields;
fields.reverse();
fields
},
}
FlowStmts_: FlowStmtsRaw<'input> = {
<Sep<FlowStmts_, FlowStmtInner, ";">> => <>
}
CreateKind: CreateKind = {
"connector" => CreateKind::Connector,
"pipeline" => CreateKind::Pipeline,
}
FlowStmtInner: FlowStmtRaw<'input> = {
<Define> => <>,
<Create> => FlowStmtRaw::Create(<>),
<Connect> => FlowStmtRaw::Connect(<>),
<Use> => FlowStmtRaw::Use(<>)
}
Define: FlowStmtRaw<'input> = {
DefinePipeline => FlowStmtRaw::PipelineDefinition(<>),
DefineConnector => FlowStmtRaw::ConnectorDefinition(<>),
}
Create: CreateStmtRaw<'input> = {
<start:@L> "create" <kind:CreateKind> <id:Ident> "from" <target:ModularTarget> <params:CreationWithEnd> <end:@L> => CreateStmtRaw { mid: NodeMeta::new_box(start, end), id, target, params, kind },
<start:@L> "create" <kind:CreateKind> <id:Ident> <params:CreationWithEnd> <end:@L> => CreateStmtRaw { mid: NodeMeta::new_box(start, end), id: id.clone(), target: id.into(), params, kind },
}
Connect: ConnectStmtRaw<'input> = {
<start:@L> "connect" "/" <from:ConnectFromConnector> "to" "/" <to:ConnectToPipeline> <end:@L> => ConnectStmtRaw::ConnectorToPipeline{mid: NodeMeta::new_box(start, end), from, to},
<start:@L> "connect" "/" <from:ConnectFromPipeline> "to" "/" <to:ConnectToConnector> <end:@L> => ConnectStmtRaw::PipelineToConnector{mid: NodeMeta::new_box(start, end), from, to},
<start:@L> "connect" "/" <from:ConnectFromPipeline> "to" "/" <to:ConnectToPipeline> <end:@L> => ConnectStmtRaw::PipelineToPipeline{mid: NodeMeta::new_box(start, end), from, to}
}
ConnectFromConnector: DeployEndpointRaw<'input> = {
<start:@L> "connector" "/" <alias:Ident> <port_pos:@L> <port:MaybePort> <end:@L> => DeployEndpointRaw{mid: NodeMeta::new_box(start, end),alias, port: port.unwrap_or_else(|| IdentRaw::literal(NodeMeta::new_box(port_pos, port_pos), "out"))},
}
ConnectFromPipeline: DeployEndpointRaw<'input> = {
<start:@L> "pipeline" "/" <alias:Ident> <port_pos:@L> <port:MaybePort> <end:@L> => DeployEndpointRaw{mid: NodeMeta::new_box(start, end), alias, port: port.unwrap_or_else(|| IdentRaw::literal(NodeMeta::new_box(port_pos, port_pos), "out"))},
}
ConnectToPipeline: DeployEndpointRaw<'input> = {
<start:@L> "pipeline" "/" <alias:Ident> <port_pos:@L> <port:MaybePort> <end:@L> => DeployEndpointRaw{mid: NodeMeta::new_box(start, end), alias, port: port.unwrap_or_else(|| IdentRaw::literal(NodeMeta::new_box(port_pos, port_pos), "in"))},
}
ConnectToConnector: DeployEndpointRaw<'input> = {
<start:@L> "connector" "/" <alias:Ident> <port_pos:@L> <port:MaybePort> <end:@L> => DeployEndpointRaw{mid: NodeMeta::new_box(start, end), alias, port: port.unwrap_or_else(|| IdentRaw::literal(NodeMeta::new_box(port_pos, port_pos), "in"))},
}
DefineConnector: ConnectorDefinitionRaw<'input> = {
// Connectors - these define connectors for deployment
<start:@L> <docs:(DocComment)?> "define" "connector" <id:Ident> "from" <kind:ConnectorKind> <params:ArgsWithEnd> <end:@L> => ConnectorDefinitionRaw { mid: NodeMeta::new_box(start, end), id: id.to_string(), kind, params, docs },
}
DefineFlow: FlowDefinitionRaw<'input> = {
// Flow - these define how pipelines and connectors are interconnected to form a unit deployment graph
<start:@L> <doc:(DocComment)?> "define" "flow" <id:Ident> <params:DefinitionArgs> "flow" <stmts:FlowStmts> "end" <end:@L> => FlowDefinitionRaw { mid: NodeMeta::new_box(start, end), id: id.to_string(), params, stmts, doc },
}
////////////////////////////// Trickle statements //////////////////////////////
// These statements for the root of the language.
/// Root
pub(crate) Query : QueryRaw<'input> = {
<start:@L> <config:ConfigDirectives> <stmts:Stmts> <end:@L> => QueryRaw{mid: NodeMeta::new_box(start, end), config: config, stmts, params: DefinitionalArgsRaw::none(NodeMeta::new_box(start, end))},
<start:@L> <stmts:Stmts> <end:@L> => QueryRaw{mid: NodeMeta::new_box(start, end), config: vec![], stmts, params: DefinitionalArgsRaw::none(NodeMeta::new_box(start, end))},
}
Stmts: StmtsRaw<'input> = {
<stmt:Stmt> ";" <list:Stmts> => {
let mut list = list;
list.insert(0,stmt);
list
},
<item:Stmt> ";"? => vec![item],
}
Stmt: StmtRaw<'input> = {
// Generic
Use => StmtRaw::Use(<>),
// Query
DefineWindow => StmtRaw::WindowDefinition(<>),
DefineOperator => StmtRaw::OperatorDefinition(Box::new(<>)),
DefineScript => StmtRaw::ScriptDefinition(<>),
DefinePipeline => StmtRaw::PipelineDefinition(<>),
CreateOperator => <>,
CreateScript => <>,
CreatePipeline => <>,
CreateStream => <>,
OperatorSelect => <>,
}
//// DEFINEs
DefineWindow: WindowDefinitionRaw<'input> = {
<doc:(DocComment)?> <start:@L> "define" "window" <id:Ident> "from" <kind:WindowKind> <params:CreationWith> <script:(EmbeddedScriptImut)?> "end" <end:@L> => {
WindowDefinitionRaw { mid: NodeMeta::new_box(start, end), id: id.to_string(), kind, params, script, doc }
}
}
DefineOperator: OperatorDefinitionRaw<'input> = {
<doc:(DocComment)?> <start:@L> "define" "operator" <id:Ident> "from" <kind:OperatorKind> <params:ArgsWithEnd> <end:@L> => {
OperatorDefinitionRaw { mid: NodeMeta::new_box(start, end), kind, id: id.to_string(), params: params, doc }
}
}
DefineScript: ScriptDefinitionRaw<'input> = {
<doc:(DocComment)?> <start:@L> "define" "script" <id:Ident> <params:DefinitionArgs> <script:EmbeddedScript> <end:@L> => {
ScriptDefinitionRaw { mid: NodeMeta::new_box(start, end), id: id.to_string(), script, params, doc }
}
}
DefinePipeline: PipelineDefinitionRaw<'input> = {
<doc:(DocComment)?> <start:@L> "define" "pipeline" <id:Ident> <from:("from" <Ports>)?> <into:("into" <Ports>)?> <params:DefinitionArgs> <pipeline:Pipeline> <end:@L> => {
PipelineDefinitionRaw { mid: NodeMeta::new_box(start, end), id: id.to_string(), pipeline: pipeline.1, config: pipeline.0, params, from, into, doc }
}
}
//// BUILTIN OPERATORS
OperatorSelect: StmtRaw<'input> = {
<start:@L> "select" <target:ComplexExprImut> "from" <from:StreamPort> <windows:(WindowClause)?> <maybe_where:(WhereClause)?> <maybe_group_by:(GroupByClause)?> "into" <into:StreamPort> <maybe_having:(HavingClause)?> <end:@L> => StmtRaw::SelectStmt(Box::new(SelectRaw { mid: NodeMeta::new_box(start, end), from, into, target, maybe_where, maybe_having, windows, maybe_group_by})),
}
//// CREATEs
CreateStream: StmtRaw<'input> = {
<start:@L> "create" "stream" <id:Ident> <end:@L> => StmtRaw::StreamStmt(StreamStmtRaw { mid: NodeMeta::new_box(start, end), id: id.to_string() }),
}
CreateScript: StmtRaw<'input> = {
<start:@L> "create" "script" <id:Ident> <params:CreationWithEnd> <end:@L> => {
StmtRaw::ScriptCreate(ScriptCreateRaw { mid: NodeMeta::new_box(start, end), id: id.to_string(), target: id.into(), params })
},
// With ModularTarget
<start:@L> "create" "script" <id:Ident> "from" <target:ModularTarget> <params:CreationWithEnd> <end:@L> => {
StmtRaw::ScriptCreate(ScriptCreateRaw { mid: NodeMeta::new_box(start, end), id: id.to_string(), target, params })
}
}
CreateOperator: StmtRaw<'input> = {
<start:@L> "create" "operator" <id:Ident> <params:CreationWithEnd> <end:@L> => {
StmtRaw::OperatorCreate(OperatorCreateRaw { mid: NodeMeta::new_box(start, end), id: id.to_string(), target: id.into(), params })
},
// With ModulartTarget
<start:@L> "create" "operator" <id:Ident> "from" <target:ModularTarget> <params:CreationWithEnd> <end:@L> => {
StmtRaw::OperatorCreate(OperatorCreateRaw { mid: NodeMeta::new_box(start, end), id: id.to_string(), target, params })
}
}
CreatePipeline: StmtRaw<'input> = {
<start:@L> "create" "pipeline" <alias:Ident> <params:CreationWithEnd> <end:@L> => {
StmtRaw::PipelineCreate(PipelineCreateRaw { mid: NodeMeta::new_box(start, end), alias: alias.to_string(), target: alias.into(), params })
},
// With ModulartTarget
<start:@L> "create" "pipeline" <alias:Ident> "from" <target:ModularTarget> <params:CreationWithEnd> <end:@L> => {
StmtRaw::PipelineCreate(PipelineCreateRaw { mid: NodeMeta::new_box(start, end), alias: alias.to_string(), target, params })
}
}
MaybePort: Option<IdentRaw<'input>> = {
("/" <Ident>)? => <>,
}
StreamPort: (IdentRaw<'input>, Option<IdentRaw<'input>>) = {
<op:Ident> <port:MaybePort> => (op, port),
}
WindowKind: WindowKind = {
"sliding" => WindowKind::Sliding,
"tumbling" => WindowKind::Tumbling,
}
WindowClause: Vec<WindowName> = {
<WindowDefn> => <>,
}
Windows: Vec<WindowName> = {
<fields:Windows_> => {
let mut fields = fields;
fields.reverse();
fields
},
}
Windows_: Vec<WindowName> = {
<Sep<Windows_, Window, ",">> => <>
}
Window: WindowName = {
<start:@L> <id:ModularTarget> <end:@L> => WindowName { mid: NodeMeta::new_box(start, end), id},
}
WindowDefn: Vec<WindowName> = {
"[" <windows:Windows> "]" => windows
}
WhereClause: ImutExprRaw<'input> = {
"where" <ComplexExprImut> => <>,
}
HavingClause: ImutExprRaw<'input> = {
"having" <ComplexExprImut> => <>,
}
GroupByClause: GroupByRaw<'input> = {
"group" "by" <GroupDef> => <>
}
GroupDef: GroupByRaw<'input> = {
<start:@L> <expr:ExprImut> <end:@L> => GroupByRaw::Expr{expr, mid: NodeMeta::new_box(start, end)},
<start:@L> "set" "("<items:GroupDefs>")" <end:@L> => GroupByRaw::Set{items, mid: NodeMeta::new_box(start, end)},
<start:@L> "each" "("<expr:ExprImut>")" <end:@L> => GroupByRaw::Each{expr, mid: NodeMeta::new_box(start, end)},
}
GroupDefs: Vec<GroupByRaw<'input>> = {
<fields:GroupDefs_> => {
let mut fields = fields;
fields.reverse();
fields
},
}
GroupDefs_: Vec<GroupByRaw<'input>> = {
<Sep<GroupDefs_, GroupDef, ",">> => <>
}
EmbeddedScriptImut: ScriptRaw<'input> = {
"script" <EmbeddedScriptContent> => <>,
}
EmbeddedScriptContent: ScriptRaw<'input> = {
<start:@L> <expr:ExprImut> <end:@L> => ScriptRaw::new(NodeMeta::new_box(start, end), vec![TopLevelExprRaw::Expr(ExprRaw::Imut(expr))], None),
}
Ports: Vec<IdentRaw<'input>> = {
<Sep<Ports, <Ident>, ",">> => <>
}
OperatorKind: OperatorKindRaw = {
<start:@L> <module:Ident> "::" <oper:Ident> <end:@L> => OperatorKindRaw { mid: NodeMeta::new_box(start, end), module: module.id.to_string(), operation: oper.id.to_string() }
}
EmbeddedScript: ScriptRaw<'input> = {
"script" <start:@L> <exprs:TopLevelExprs> <end:@L> "end" => ScriptRaw::new(NodeMeta::new_box(start, end), exprs, None),
}
Pipeline: (ConfigRaw<'input>, StmtsRaw<'input>) = {
"pipeline"
<cfg:ConfigDirectives?>
<inner_stmts:PipelineCreateInner>
"end" => (cfg.unwrap_or_default(), inner_stmts),
}
PipelineCreateInner : StmtsRaw<'input> = {
<stmt:Stmt> ";" <list:Stmts> => {
let mut list = list;
list.insert(0,stmt);
list
},
<item:Stmt> ";"? => vec![item],
}
////////////////////////////// Tremorscript statements //////////////////////////////
// These statements for the root of the language.
pub(crate) Script : ScriptRaw<'input> = {
<start:@L> <doc:(ModComment)?> <exprs:TopLevelExprs> <end:@L> => ScriptRaw::new(NodeMeta::new_box(start, end), exprs, doc),
}
/// List of expressions at the root of the script
TopLevelExprs: TopLevelExprsRaw<'input> = {
<expr:TopLevelExpr> ";" <list:TopLevelExprs> => {
let mut list = list;
list.insert(0,expr);
list
},
<item:TopLevelExpr> ";"? => vec![item],
}
/// List of expressions at the root of the script
InnerExprs: ExprsRaw<'input> = {
<expr:Expr> ";" <list:InnerExprs> => {
let mut list = list;
list.insert(0,expr);
list
},
<item:Expr> ";"? => vec![item],
}
TopLevelExpr: TopLevelExprRaw<'input> = {
Const => TopLevelExprRaw::Const(<>),
FnDefn => TopLevelExprRaw::FnDefn(<>),
Intrinsic => TopLevelExprRaw::FnDefn(<>),
Expr => TopLevelExprRaw::Expr(<>),
Use => TopLevelExprRaw::Use(<>),
}
/// A const expressions (this gets compiled out during the 2nd phase)
Const: ConstRaw<'input> = {
<comment:(DocComment)?> <start:@L> "const" <name:Ident> @L "=" @R <expr:ComplexExprImut> <end:@L> => ConstRaw{name: name.id, expr: expr, mid: NodeMeta::new_box(start, end), comment},
}
////////////////////////////// Expr (mutable) //////////////////////////////
// Expressions are the fundamental building block of the language
// starting from binary operators to more complex things like match
// statements
/// Expression root
Expr: ExprRaw<'input> = {
SimpleExpr => <>,
}
/// Non precedence based expressions and language constructs
SimpleExpr: ExprRaw<'input> = {
<pp:Match> => ExprRaw::MatchExpr(Box::new(pp)),
<comprehension:For> => ExprRaw::Comprehension(Box::new(comprehension)),
Let => <>,
Drop => <>,
Emit => <>,
ExprImut => <>.into(),
}
/// Part of the expressions that are always immutable
AlwaysImutExpr: ImutExprRaw<'input> = {
<pp:Patch> => ImutExprRaw::Patch(Box::new(pp)),
<pp:Merge> => ImutExprRaw::Merge(Box::new(pp)),
<call:Invoke> => ImutExprRaw::Invoke(call),
<literal:Literal> => ImutExprRaw::Literal(literal),
<path:Path> => ImutExprRaw::Path(path),
<record:Record> => ImutExprRaw::Record(Box::new(record)),
<list:List> => ImutExprRaw::List(Box::new(list)),
<s:StringLiteral> => ImutExprRaw::String(s),
<b:BytesLiteral> => ImutExprRaw::Bytes(b),
<r:Recur> => ImutExprRaw::Recur(r),
}
Recur: RecurRaw<'input> = {
<start:@L> "recur" "(" ")" <end:@L> => RecurRaw{mid: NodeMeta::new_box(start, end), exprs: vec![]},
<start:@L> "recur" "(" <args:InvokeArgs> ")" <end:@L> => RecurRaw{mid: NodeMeta::new_box(start, end), exprs: args},
}
////////////////////////////// Expr (immutable) ////////////////////////////////////////
// Immutable expressions are used in parts of the language that are not supposed to
// modify the event, locals, or metadata.
/// Expression root
ExprImut: ImutExprRaw<'input> = {
OrExprImut => <>,
}
/// Logical or
OrExprImut: ImutExprRaw<'input> = {
/// TODO should be OrExprImut?
<o:BinOp<BinOr, ExprImut, XorExprImut>> => <>,
XorExprImut => <>,
}
/// Logical xor
XorExprImut: ImutExprRaw<'input> = {
<o:BinOp<BinXor, XorExprImut, AndExprImut>> => <>,
AndExprImut => <>,
}
/// Logical and
AndExprImut: ImutExprRaw<'input> = {
BinOp<BinAnd, AndExprImut, BitOrExprImut> => <>,
BitOrExprImut => <>,
}
/// Bitwise or
BitOrExprImut: ImutExprRaw<'input> = {
BitXorExprImut => <>,
}
/// Bitwise xor
BitXorExprImut: ImutExprRaw<'input> = {
<o:BinOp<BinBitXor, BitXorExprImut, BitAndExprImut>> => <>,
BitAndExprImut => <>,
}
/// Bitwise and
BitAndExprImut: ImutExprRaw<'input> = {
<o:BinOp<BinBitAnd, BitAndExprImut, EqExprImut>> => <>,
EqExprImut => <>,
}
/// Equality comparison operators
EqExprImut: ImutExprRaw<'input> = {
BinOp<BinEq, EqExprImut, CmpExprImut> => <>,
CmpExprImut => <>,
}
/// Relational comparison operators
CmpExprImut: ImutExprRaw<'input> = {
BinOp<BinCmp, CmpExprImut, BitShiftExprImut> => <>,
BitShiftExprImut => <>,
}
/// Bit shift operators (>>, >>>, <<)
BitShiftExprImut: ImutExprRaw<'input> = {
BinOp<BinBitShift, BitShiftExprImut, AddExprImut> => <>,
AddExprImut => <>,
}
/// Addition based math (+, -)
AddExprImut: ImutExprRaw<'input> = {
BinOp<BinAdd, AddExprImut, MulExprImut> => <>,
MulExprImut => <>,
}
/// Multiplication based math (*, /, %)
MulExprImut: ImutExprRaw<'input> = {
BinOp<BinMul, MulExprImut, UnaryExprImut> => <>,
UnaryExprImut => <>,
}
/// Mathematical unary expressions (unary +, unary - )
UnaryExprImut: ImutExprRaw<'input> = {
<start:@L> "+" <expr:UnaryExprImut> <end:@L> => ImutExprRaw::Unary(Box::new(UnaryExprRaw { kind: UnaryOpKind::Plus, expr: expr, mid: NodeMeta::new_box(start, end) })),
<start:@L> "-" <expr:UnaryExprImut> <end:@L> => ImutExprRaw::Unary(Box::new(UnaryExprRaw { kind: UnaryOpKind::Minus, expr: expr, mid: NodeMeta::new_box(start, end) })),
UnarySimpleExprImut => <>,
}
/// Logical and bitwise unary expressions (not, !)
UnarySimpleExprImut: ImutExprRaw<'input> = {
/// TODO ask about change from PresenceSimplExprImut
/// also should include these on the same level as unary +, -?
<start:@L> "not" <expr:UnarySimpleExprImut> <end:@L> => ImutExprRaw::Unary(Box::new(UnaryExprRaw { kind: UnaryOpKind::Not, expr: expr, mid: NodeMeta::new_box(start, end) })),
<start:@L> "!" <expr:UnarySimpleExprImut> <end:@L> => ImutExprRaw::Unary(Box::new(UnaryExprRaw { kind: UnaryOpKind::BitNot, expr: expr, mid: NodeMeta::new_box(start, end) })),
PresenceSimplExprImut => <>,
}
/// Presence operations (present, absent)
PresenceSimplExprImut: ImutExprRaw<'input> = {
<start:@L> "present" <path:Path> <end:@L> => ImutExprRaw::Present{path, mid: NodeMeta::new_box(start, end)},
/// TODO link to !?
<start:@L> "absent" <path:Path> <end:@L> => ImutExprRaw::Unary(Box::new(UnaryExprRaw { kind: UnaryOpKind::Not, expr: ImutExprRaw::Present{path, mid: NodeMeta::new_box(start, end)}, mid: NodeMeta::new_box(start, end) })),
SimpleExprImut => <>,
}
/// Immutable language constructs that can be used without having to worry about
/// sideefects
ComplexExprImut: ImutExprRaw<'input> = {
<pp:MatchImut> => ImutExprRaw::Match(Box::new(pp)),
<comprehension:ForImut> => ImutExprRaw::Comprehension(Box::new(comprehension)),
ExprImut => <>
}
Intrinsic: AnyFnRaw<'input> = {
<doc:(DocComment)?> <start:@L> "intrinsic" "fn" <name:Ident> "(" ")" "as" <imod:ModularTarget> <end:@L> => {
let invoce_args = vec![];
let mut imod = imod;
let mut module = vec!["core".to_string()];
module.append(&mut imod.module);
let invoke = InvokeRaw{
mid: NodeMeta::new_box(start, end),
module,
fun: imod.id,
args: invoce_args
};
let body = vec![ExprRaw::Imut(ImutExprRaw::Invoke(invoke))];
AnyFnRaw::Normal(FnDefnRaw{name, args: vec![], body, mid: NodeMeta::new_box(start, end), doc, open: false, inline: true})
},
<doc:(DocComment)?> <start:@L> "intrinsic" "fn" <name:Ident> "(" <args:FnArgs> ")" "as" <imod:ModularTarget><end:@L> => {
let invoce_args = args.iter().map(|root|
ImutExprRaw::Path(
PathRaw::Local(
LocalPathRaw{mid: root.mid.clone(), segments: vec![], root: root.clone()}
))).collect();
let mut imod = imod;
let mut module = vec!["core".to_string()];
module.append(&mut imod.module);
let invoke = InvokeRaw{
mid: NodeMeta::new_box(start, end),
module,
fun: imod.id,
args: invoce_args
};
let body = vec![ExprRaw::Imut(ImutExprRaw::Invoke(invoke))];
AnyFnRaw::Normal(FnDefnRaw{name, args, body, mid: NodeMeta::new_box(start, end), doc, open: false, inline: true})
},
<doc:(DocComment)?> <start:@L> "intrinsic" "fn" <name:Ident> "(" <args:FnArgs> "," "." "." "." ")" "as" <imod:ModularTarget> <end:@L> => {
let invoce_args = args.iter().map(|root|
ImutExprRaw::Path(
PathRaw::Local(
LocalPathRaw{mid: root.mid.clone(), segments: vec![], root: root.clone()}
))).collect();
let mut imod = imod;
let mut module = vec!["core".to_string()];
module.append(&mut imod.module);
let invoke = InvokeRaw{
mid: NodeMeta::new_box(start, end),
module,
fun: imod.id,
args: invoce_args
};
let body = vec![ExprRaw::Imut(ImutExprRaw::Invoke(invoke))];
AnyFnRaw::Normal(FnDefnRaw{name, args, body, mid: NodeMeta::new_box(start, end), doc, open: true, inline: true})
},
<doc:(DocComment)?> <start:@L> "intrinsic" "fn" <name:Ident> "(" "." "." "." ")" "as" <imod:ModularTarget> <end:@L> => {
let args = vec![];
let invoce_args = vec![];
let mut imod = imod;
let mut module = vec!["core".to_string()];
module.append(&mut imod.module);
let invoke = InvokeRaw{
mid: NodeMeta::new_box(start, end),
module,
fun: imod.id,
args: invoce_args
};
let body = vec![ExprRaw::Imut(ImutExprRaw::Invoke(invoke))];
AnyFnRaw::Normal(FnDefnRaw{name, args, body, mid: NodeMeta::new_box(start, end), doc, open: true, inline: true})
},
}
FnDefn: AnyFnRaw<'input> = {
<doc:(DocComment)?> <start:@L> "fn" <name:Ident> "(" "." "." "." ")" "with" <body:InnerExprs> "end" <end:@L> => AnyFnRaw::Normal(FnDefnRaw{name, args: vec![], body, mid: NodeMeta::new_box(start, end), doc, open: true, inline: false}),
<doc:(DocComment)?> <start:@L> "fn" <name:Ident> "(" <args:FnArgs> "," "." "." "." ")" "with" <body:InnerExprs> "end" <end:@L> => AnyFnRaw::Normal(FnDefnRaw{name, args, body, mid: NodeMeta::new_box(start, end), doc, open: true, inline: false}),
<doc:(DocComment)?> <start:@L> "fn" <name:Ident> "(" ")" "with" <body:InnerExprs> "end" <end:@L> => AnyFnRaw::Normal(FnDefnRaw{name, args: vec![], body, mid: NodeMeta::new_box(start, end), doc, open: false, inline: false}),
<doc:(DocComment)?> <start:@L> "fn" <name:Ident> "(" <args:FnArgs> ")" "with" <body:InnerExprs> "end" <end:@L> => AnyFnRaw::Normal(FnDefnRaw{name, args, body, mid: NodeMeta::new_box(start, end), doc, open: false, inline: false}),
<doc:(DocComment)?> <start:@L> "fn" <name:Ident> "(" ")" "of" <cases:FnCases> "end" <end:@L> => AnyFnRaw::Match(MatchFnDefnRaw{name, args: vec![], mid: NodeMeta::new_box(start, end), cases, doc, open: false, inline: false}),
<doc:(DocComment)?> <start:@L> "fn" <name:Ident> "(" <args:FnArgs> ")" "of" <cases:FnCases> "end" <end:@L> => AnyFnRaw::Match(MatchFnDefnRaw{name, args, mid: NodeMeta::new_box(start, end), cases, doc, open: false, inline: false}),
}
FnCases: Vec<PredicateClauseRaw<'input, ExprRaw<'input>>> = {
<cases:FnCaseClauses> <default:FnCaseDefault> => {
let mut cases = cases;
cases.push(default);
cases
},
<default:FnCaseDefault> => {
let mut cases = Vec::new();
cases.push(default);
cases
}
}
FnCaseDefault: PredicateClauseRaw<'input, ExprRaw<'input>> = {
<start:@L> "default" <exprs:Effectors> <end:@L> => {
PredicateClauseRaw { pattern: PatternRaw::Default, exprs, guard: None, mid: NodeMeta::new_box(start, end)}
}
}
FnCase: PredicateClauseRaw<'input, ExprRaw<'input>> = {
<start:@L> "case" <pstart:@L> "(" <pattern:ArrayPredicatePatterns> ")" <guard:WhenClause> <pend:@L> <exprs:Effectors> <end:@L> => PredicateClauseRaw {
pattern: PatternRaw::Assign(AssignPatternRaw {
id: FN_RES_NAME.into(),
pattern: Box::new(PatternRaw::Tuple(TuplePatternRaw{exprs: pattern, mid: NodeMeta::new_box(pstart, pend), open: false}))
}),
exprs,
guard,
mid: NodeMeta::new_box(start, end)
},
}
FnCaseClauses: Vec<PredicateClauseRaw<'input, ExprRaw<'input>>> = {
<e:FnCase> => vec![e],
<v:FnCaseClauses> <e:FnCase> => {
let mut v = v;
v.push(e);
v
},
}
FnArgs: Vec<IdentRaw<'input>> = {
<e:Ident> => vec![e],
<v:FnArgs> "," <e:Ident> => {
let mut v = v;
v.push(e);
v
},
}
SimpleExprImut: ImutExprRaw<'input> = {
"(" <expr:ComplexExprImut> ")" => expr,
AlwaysImutExpr => <>
}
////////////////////////////// Literals and semi literals //////////////////////////////
// This section covers literals (strings, nil, etc) and semi literals.
// At this stage Lists and Records are considered semi literals
// as we only decide in the next step if they are literals or include
// variables and need to get constructed during runtime.
/// Basic literals
Literal: LiteralRaw<'input> = {
<start:@L> <v:Nil> <end:@L> => LiteralRaw{value: v, mid: NodeMeta::new_box(start, end)},
<start:@L> <v:Bool> <end:@L> => LiteralRaw{value: v, mid: NodeMeta::new_box(start, end)},
<start:@L> <v:Int> <end:@L> => LiteralRaw{value: v, mid: NodeMeta::new_box(start, end)},
<start:@L> <v:Float> <end:@L> => LiteralRaw{value: v, mid: NodeMeta::new_box(start, end)},
}
/// null
Nil: Value<'input> = {
<_literal:"nil"> => Value::null(),
}
/// booleans
Bool: Value<'input> = {
<literal:"bool"> => Value::from(literal),
}
/// integers
Int: Value<'input> = {
<literal:"int"> => Value::from(literal),
}
/// floats
Float: Value<'input> = {
<literal:"float"> => Value::from(literal.0),
}
StringLiteral: StringLitRaw<'input> = {
<start:@L> "heredoc_start" <elements:StrLitElements> "heredoc_end" <end:@L> => {
StringLitRaw { mid: NodeMeta::new_box(start, end), elements }
},
<start:@L> "\"" <elements:StrLitElements> "\"" <end:@L> => {
StringLitRaw { mid: NodeMeta::new_box(start, end), elements }
},
<start:@L> "\"" "\"" <end:@L> => {
StringLitRaw {
mid: NodeMeta::new_box(start, end),
elements: vec!["".into()],
}
},
}
StrLitElements: StrLitElementsRaw<'input> = {
<literal:StringPart> <es:StrLitElements> => {
let mut es = es;
es.push(literal.into());
es
},
"\\#" <es:StrLitElements> => {
let mut es = es;
es.push("#".into());
es
},
"#{" <expr:ExprImut> "}" <es:StrLitElements> => {
let mut es = es;
es.push(expr.into());
es
},
<literal:StringPart> => vec![literal.into()],
"\\#" => vec!["#".into()],
"#{" <expr:ExprImut> "}" => vec![expr.into()],
}
StringPart: Cow<'input, str> = {
"string" => <>,
"heredoc" => <>
}
/// array semi literals
List: ListRaw<'input> = {
<start:@L> "[" <exprs:ListElements> "]" <end:@L> => ListRaw{mid: NodeMeta::new_box(start, end), exprs},
<start:@L> "[" "]" <end:@L> => ListRaw{mid: NodeMeta::new_box(start, end), exprs: vec![]},
}
ListElements: ImutExprsRaw<'input> = {
<fields:ListElements_> => {
let mut fields = fields;
fields.reverse();
fields
},
}
ListElements_: ImutExprsRaw<'input> = {
<Sep<ListElements_, ComplexExprImut, ",">> => <>,
}
/// A semi literal record
Record: RecordRaw<'input> = {
<start:@L> "{" <fields:Fields> "}" <end:@L> => RecordRaw { fields: fields, mid: NodeMeta::new_box(start, end) },
<start:@L> "{" "}" <end:@L> => RecordRaw { fields: vec![], mid: NodeMeta::new_box(start, end) }
}
/// Fields for a static record
Field: FieldRaw<'input> = {
<start:@L> <name:StringLiteral> ":" <value:ComplexExprImut> <end:@L> => {
FieldRaw { name, value, mid: NodeMeta::new_box(start, end) }
}
}
////////////////////////////// Path ////////////////////////////////////////
// Path define lookups of data that is in either local variable, metadata
// or the event.
/// A path into one of the scopes
Path: PathRaw<'input> = {
MetaPath => <>,
EventPath => <>,
StatePath => <>,
LocalPath => <>,
ConstPath => <>,
AggrPath => <>,
ArgsPath => <>,
ExprPath => <>,
}
ExprPathRoot: ImutExprRaw<'input> = {
// We allow any complex expression when encased in parens
"(" <expr: ComplexExprImut> ")" => expr,
// Otherwise we allow invocations (function calls)
<call:Invoke> => ImutExprRaw::Invoke(call),
// records
<record:Record> => ImutExprRaw::Record(Box::new(record)),
// lists
<list:List> => ImutExprRaw::List(Box::new(list)),
// we could allow other things like match / patch / merge
// but it's less readable to have `match ... end.something`
// then `(match ... end).something`
//
// Also we technically could use any literal as root
// but we know that something like `null[1]` will always
// fail so we might as well forbid it at expression
}
ExprPath: PathRaw<'input> = {
<start:@L> <expr:ExprPathRoot> <segments:PathSegments> <end:@L> => {
let expr = Box::new(expr);
PathRaw::Expr(ExprPathRaw { expr, segments, mid: NodeMeta::new_box(start, end) })
}
}
/// A metadata path
MetaPath: PathRaw<'input> = {
<start:@L> "$" <root:Ident> <segments:PathSegments> <end:@L> => {
let mut segments = segments;
segments.insert(0, root.into());
PathRaw::Meta(MetadataPathRaw { segments, mid: NodeMeta::new_box(start, end) })
},
<start:@L> "$" <root:Ident> <end:@L> => {
PathRaw::Meta(MetadataPathRaw { segments: vec![root.into()], mid: NodeMeta::new_box(start, end) })
},
<start:@L> "$" <end:@L> => PathRaw::Meta(MetadataPathRaw { segments: vec![], mid: NodeMeta::new_box(start, end) }),
}
/// A window/group path
AggrPath: PathRaw<'input> = {
<start:@L> "group" <segments:PathSegments> <end:@L> => {
PathRaw::Reserved(ReservedPathRaw::Group { segments, mid: NodeMeta::new_box(start, end) })
},
<start:@L> "group" <end:@L> => PathRaw::Reserved(ReservedPathRaw::Group { segments: vec![], mid: NodeMeta::new_box(start, end) }),
<start:@L> "window" <segments:PathSegments> <end:@L> => {
PathRaw::Reserved(ReservedPathRaw::Window { segments, mid: NodeMeta::new_box(start, end) })
},
<start:@L> "window" <end:@L> => PathRaw::Reserved(ReservedPathRaw::Window { segments: vec![], mid: NodeMeta::new_box(start, end) }),
}
/// Arguments to a script or function
ArgsPath: PathRaw<'input> = {
<start:@L> "args" <segments:PathSegments> <end:@L> => {
PathRaw::Reserved(ReservedPathRaw::Args { segments, mid: NodeMeta::new_box(start, end) })
},
<start:@L> "args" <end:@L> => PathRaw::Reserved(ReservedPathRaw::Args { segments: vec![], mid: NodeMeta::new_box(start, end) }),
}
/// Local path
LocalPath: PathRaw<'input> = {
<start:@L> <root:Ident> <segments:PathSegments> <end:@L> => {
PathRaw::Local(LocalPathRaw { root, segments, mid: NodeMeta::new_box(start, end) })
},
<start:@L> <root:Ident> <end:@L> => {
PathRaw::Local(LocalPathRaw { segments: vec![], root, mid: NodeMeta::new_box(start, end) })
},
}
/// Const path - absolute path prefixed with module
ConstPath: PathRaw<'input> = {
<start:@L> <module:ModPath> "::" <path:LocalPath> <end:@L> => {
if let PathRaw::Local(LocalPathRaw { root, segments, .. }) = path {
PathRaw::Const(ConstPathRaw { module, root, segments, mid: NodeMeta::new_box(start, end) })
} else {
unreachable!()
}
},
}
/// State path
StatePath: PathRaw<'input> = {
<start:@L> "state" <segments:PathSegments> <end:@L> => PathRaw::State(StatePathRaw { segments, mid: NodeMeta::new_box(start, end) }),
<start:@L> "state" <end:@L> => PathRaw::State(StatePathRaw { segments: vec![], mid: NodeMeta::new_box(start, end) }),
}
/// Event path
EventPath: PathRaw<'input> = {
<start:@L> "event" <segments:PathSegments> <end:@L> => PathRaw::Event(EventPathRaw { segments, mid: NodeMeta::new_box(start, end) }),
<start:@L> "event" <end:@L> => PathRaw::Event(EventPathRaw { segments: vec![], mid: NodeMeta::new_box(start, end) }),
}
/// Segments of a path
PathSegments: SegmentsRaw<'input> = {
"." <segment:Ident> <segments:PathSegments> => {
let mut segments = segments;
segments.insert(0, segment.into());
segments
},
"[" <selector:Selector> "]" <segments:PathSegments> => {
let mut segments = segments;
segments.insert(0, selector);
segments
},
"[" <selector:Selector> "]" => vec![selector],
"." <segment:Ident> => vec![segment.into()],
}
/// A path selector aka [...]
Selector: SegmentRaw<'input> = {
<range_start:ComplexExprImut> ":" <range_end:ComplexExprImut> => {
SegmentRaw::Range(Box::new(SegmentRangeRaw{
range_start,
range_end,
}))
},
<start:@L> <expr:ComplexExprImut> <end:@L> => SegmentRaw::Element(Box::new(SegmentElementRaw{expr, mid: NodeMeta::new_box(start, end)})),
}
////////////////////////////// Function invocations //////////////////////////////
// Function invocations consist of a module, function name and an
// argument list.
/// Function invocation
Invoke: InvokeRaw<'input> = {
<start:@L> <fun:FunctionName> "(" <args:InvokeArgs> ")" <end:@L> => InvokeRaw { module: fun.0, fun: fun.1, args, mid: NodeMeta::new_box(start, end) },
<start:@L> <fun:FunctionName> "(" ")" <end:@L> => InvokeRaw { module: fun.0, fun: fun.1, args: vec![], mid: NodeMeta::new_box(start, end) },
}
FunctionName: (Vec<String>, String) = {
<fun:Ident> => (vec![], fun.id.to_string()),
<p:ModPath> "::" <fun:Ident> => (p.iter().map(|i| i.id.to_string()).collect(), fun.id.to_string()),
}
ModPath: Vec<IdentRaw<'input>> = {
<v:ModPath> "::" <e:Ident> => {
let mut v = v;
v.push(e);
v
},
<e:Ident> => vec![e],
}
InvokeArgs: ImutExprsRaw<'input> = {
<fields:InvokeArgs_> => {
let mut fields = fields;
fields.reverse();
fields
},
}
InvokeArgs_: ImutExprsRaw<'input> = {
<Sep<InvokeArgs_, ComplexExprImut, ",">> => <>,
}
////////////////////////////// Terminal expressions //////////////////////////////
// These expressions terminate execution.
/// drop the current event.
Drop: ExprRaw<'input> = {
<start:@L> "drop" <end:@L> => ExprRaw::Drop{mid: NodeMeta::new_box(start, end) } ,
}
/// emits the current event to a given output
Emit: ExprRaw<'input> = {
<start:@L> "emit" <expr:ComplexExprImut> "=>" <port:StringLiteral> <end:@L> => ExprRaw::Emit(Box::new(EmitExprRaw { expr: expr, mid: NodeMeta::new_box(start, end), port: Some(ImutExprRaw::String(port)) })),
<start:@L> "emit" <expr:ComplexExprImut> <end:@L> => ExprRaw::Emit(Box::new(EmitExprRaw { expr: expr, mid: NodeMeta::new_box(start, end), port: None })),
<start:@L> "emit" "=>" <port:StringLiteral> <end:@L> => ExprRaw::Emit(Box::new(EmitExprRaw { expr: ImutExprRaw::Path(PathRaw::Event(EventPathRaw { segments: vec![], mid: NodeMeta::new_box(start, end) })).into(), mid: NodeMeta::new_box(start, end), port: Some(ImutExprRaw::String(port)) })) ,
<start:@L> "emit" <end:@L> => ExprRaw::Emit(Box::new(EmitExprRaw { expr: ImutExprRaw::Path(PathRaw::Event(EventPathRaw { segments: vec![], mid: NodeMeta::new_box(start, end) })).into(), mid: NodeMeta::new_box(start, end), port: None })),
}
////////////////////////////// Let expression //////////////////////////////
// An expression that assigns a value to a variable
/// Let expression to assign values
Let: ExprRaw<'input> = {
"let" <expr:Assignment> => expr, // Expr::Let(Let { exprs: vec![expr], mid: NodeMeta::new_box(start, end) }),
}
Assignment: ExprRaw<'input> = {
<start:@L> <path:Path> @L "=" @R <expr:SimpleExpr> <end:@L> => ExprRaw::Assign(Box::new(AssignRaw { path, expr: expr, mid: NodeMeta::new_box(start, end) })),
}
////////////////////////////// patch expression //////////////////////////////
// Patches a input value with a set of instructions
Patch: PatchRaw<'input> = {
// NOTE: ASSUMPTION array and literal targets are banned
<start:@L> "patch" <target:ComplexExprImut> "of" <operations:PatchOperations> "end" <end:@L> => PatchRaw { target, operations, mid: NodeMeta::new_box(start, end) }
}
PatchOperations: PatchOperationsRaw<'input> = {
<e:PatchOperationClause> => vec![e],
<v:PatchOperations> ";" <e:PatchOperationClause> => {
let mut v = v;
v.push(e);
v
}
}
PatchField: StringLitRaw<'input> = {
StringLiteral => <>
}
PatchOperationClause: PatchOperationRaw<'input> = {
<start: @L> "insert" <ident:PatchField> "=>" <expr:ComplexExprImut> <end:@L>=> PatchOperationRaw::Insert{ ident, expr, mid: NodeMeta::new_box(start, end) },
<start: @L> "upsert" <ident:PatchField> "=>" <expr:ComplexExprImut> <end:@L>=> PatchOperationRaw::Upsert{ ident, expr, mid: NodeMeta::new_box(start, end) },
<start: @L> "update" <ident:PatchField> "=>" <expr:ComplexExprImut> <end:@L>=> PatchOperationRaw::Update{ ident, expr, mid: NodeMeta::new_box(start, end) },
<start: @L> "erase" <ident:PatchField> <end:@L>=> PatchOperationRaw::Erase{ ident, mid: NodeMeta::new_box(start, end) },
<start: @L> "move" <from:PatchField> "=>" <to:PatchField> <end:@L>=> PatchOperationRaw::Move{ from, to, mid: NodeMeta::new_box(start, end) },
<start: @L> "copy" <from:PatchField> "=>" <to:PatchField> <end:@L> => PatchOperationRaw::Copy{ from, to, mid: NodeMeta::new_box(start, end) },
<start: @L> "merge" <ident:PatchField> "=>" <expr:ComplexExprImut> <end:@L>=> PatchOperationRaw::Merge { ident, expr, mid: NodeMeta::new_box(start, end) },
<start: @L> "merge" "=>" <expr:ComplexExprImut> <end:@L>=> PatchOperationRaw::MergeRecord { expr, mid: NodeMeta::new_box(start, end) },
<start: @L> "default" <ident:PatchField> "=>" <expr:ComplexExprImut> <end:@L>=> PatchOperationRaw::Default { ident, expr, mid: NodeMeta::new_box(start, end) },
<start: @L> "default" "=>" <expr:ComplexExprImut> <end:@L>=> PatchOperationRaw::DefaultRecord { expr, mid: NodeMeta::new_box(start, end) },
}
////////////////////////////// merge expression //////////////////////////////
// Merges a perge spec (record) into the target
Merge: MergeRaw<'input> = {
<start:@L> "merge" <target:ComplexExprImut> "of" <expr:ComplexExprImut> "end" <end:@L> => MergeRaw { target, expr, mid: NodeMeta::new_box(start, end) }
}
////////////////////////////// for comprehension (mut) //////////////////////////////
// Merges a perge spec (record) into the target
For: ComprehensionRaw<'input, ExprRaw<'input>> = {
<start:@L> "for" <target:ComplexExprImut> "of" <cases:ForCaseClauses> "end" <end:@L> => ComprehensionRaw{target, cases, mid: NodeMeta::new_box(start, end)},
}
ForCaseClauses: ComprehensionCasesRaw<'input, ExprRaw<'input>> = {
<e:ForCaseClause> => vec![e],
<v:ForCaseClauses> <e:ForCaseClause> => {
let mut v = v;
v.push(e);
v
}
}
ForCaseClause: ComprehensionCaseRaw<'input, ExprRaw<'input>> = {
"case" <start:@L> "(" <k:Ident> "," <v:Ident> ")" <end:@L> <guard:WhenClause> <exprs:Effectors> => ComprehensionCaseRaw { key_name: k.id, value_name: v.id, exprs, guard, mid: NodeMeta::new_box(start, end) },
}
// ////////////////////////////// for comprehension (imutmut) //////////////////////////////
// // Merges a perge spec (record) into the target
ForImut: ComprehensionRaw<'input, ImutExprRaw<'input>> = {
<start:@L> "for" <target:ComplexExprImut> "of" <cases:ForCaseClausesImut> "end" <end:@L> => ComprehensionRaw{target, cases, mid: NodeMeta::new_box(start, end)},
}
ForCaseClausesImut: ComprehensionCasesRaw<'input, ImutExprRaw<'input>> = {
<e:ForCaseClauseImut> => vec![e],
<v:ForCaseClausesImut> <e:ForCaseClauseImut> => {
let mut v = v;
v.push(e);
v
}
}
ForCaseClauseImut: ComprehensionCaseRaw<'input, ImutExprRaw<'input>> = {
"case" <start:@L> "(" <k:Ident> "," <v:Ident> ")" <end:@L> <guard:WhenClause> <exprs:EffectorsImut> => ComprehensionCaseRaw { key_name: k.id, value_name: v.id, exprs, guard, mid: NodeMeta::new_box(start, end) },
}
////////////////////////////// match expression (mutating) //////////////////////////////
// An expression that assigns a value to a variable
/// A match expression
Match: MatchRaw<'input, ExprRaw<'input>> = {
<start:@L> "match" <target:ComplexExprImut> "of" <patterns:Predicates> "end" <end:@L> => MatchRaw { target, patterns, mid: NodeMeta::new_box(start, end) },
}
/// Predicates for a match statement (aka list of case statements)
Predicates: PredicatesRaw<'input, ExprRaw<'input>> = {
<e:PredicateClause> => vec![e],
<v:Predicates> <e:PredicateClause> => {
let mut v = v;
v.push(e);
v
}
}
/// A case statement
PredicateClause: PredicateClauseRaw<'input, ExprRaw<'input>> = {
<start:@L> "case" <pattern:CasePattern> <guard:WhenClause> <exprs:Effectors> <end:@L> => PredicateClauseRaw { pattern, exprs, guard, mid: NodeMeta::new_box(start, end) },
<start:@L> "default" <exprs:Effectors> <end:@L> => PredicateClauseRaw { pattern: PatternRaw::Default, exprs, guard: None, mid: NodeMeta::new_box(start, end) },
}
/// Effoectors aka (=> ... )
Effectors: ExprsRaw<'input> = {
"=>" <exprs:Block> => exprs
}
/// The effectors for a case statement
Block: ExprsRaw<'input> = {
<e:Expr> => vec![e],
<v:Block> ";" <e:Expr> => {
let mut v = v;
v.push(e);
v
}
}
////////////////////////////// match expression (non mutating) //////////////////////////////
// An expression that assigns a value to a variable
/// A match expression
MatchImut: MatchRaw<'input, ImutExprRaw<'input>> = {
<start:@L> "match" <target:ComplexExprImut> "of" <patterns:PredicatesImut> "end" <end:@L> => MatchRaw { target, patterns, mid: NodeMeta::new_box(start, end) },
}
/// Predicates for a match statement (aka list of case statements)
PredicatesImut: PredicatesRaw<'input, ImutExprRaw<'input>> = {
<e:PredicateClauseImut> => vec![e],
<v:PredicatesImut> <e:PredicateClauseImut> => {
let mut v = v;
v.push(e);
v
}
}
CasePattern: PatternRaw<'input> = {
<RecordPattern> => PatternRaw::Record(<>),
<ArrayPattern> => PatternRaw::Array(<>),
<TuplePattern> => PatternRaw::Tuple(<>),
<ComplexExprImut> => PatternRaw::Expr(<>),
"_" => PatternRaw::DoNotCare,
"~" <TestExpr> => PatternRaw::Extract(<>),
<segment:Ident> "=" <pattern:CasePattern> => PatternRaw::Assign(AssignPatternRaw { id: segment.id, pattern: Box::new(pattern) }),
}
/// A case statement
PredicateClauseImut: PredicateClauseRaw<'input, ImutExprRaw<'input>> = {
<start:@L> "case" <pattern:CasePattern> <guard:WhenClause> <exprs:EffectorsImut> <end:@L> => PredicateClauseRaw { pattern, exprs, guard, mid: NodeMeta::new_box(start, end) },
<start:@L> "default" <exprs:EffectorsImut> <end:@L> => PredicateClauseRaw { pattern: PatternRaw::Default, exprs, guard: None, mid: NodeMeta::new_box(start, end) },
}
/// Effoectors aka (=> ... )
EffectorsImut: ImutExprsRaw<'input> = {
"=>" <exprs:BlockImut> => exprs
}
/// The effectors for a case statement
BlockImut: ImutExprsRaw<'input> = {
<e:ComplexExprImut> => vec![e],
<v:BlockImut> "," <e:ComplexExprImut> => {
let mut v = v;
v.push(e);
v
}
}
////////////////////////////// match expression (shared) //////////////////////////////
// An expression that assigns a value to a variable
/// When part of an expression (when ...)
WhenClause: Option<ImutExprRaw<'input>> = {
("when" <ComplexExprImut>)? => <>,
}
/// Predicate Patterns (aka tests) for record pattrens
PredicateFieldPattern: PredicatePatternRaw<'input> = {
<lhs:Ident> "~=" <expr:TestExpr> => PredicatePatternRaw::TildeEq { assign: lhs.id.clone(), lhs: lhs.id, test: expr},
<assign:Ident> "=" <lhs:Ident> "~=" <expr:TestExpr> => PredicatePatternRaw::TildeEq { assign: assign.id, lhs: lhs.id, test: expr} ,
<lhs:Ident> "~=" <rp:RecordPattern> => PredicatePatternRaw::RecordPatternEq { lhs: lhs.id, pattern: rp },
<lhs:Ident> "~=" <ap:ArrayPattern> => PredicatePatternRaw::ArrayPatternEq { lhs: lhs.id, pattern: ap },
<lhs:Ident> "~=" <ap:TuplePattern> => PredicatePatternRaw::TuplePatternEq { lhs: lhs.id, pattern: ap },
"present" <lhs:Ident> => PredicatePatternRaw::FieldPresent { lhs: lhs.id },
"absent" <lhs:Ident> => PredicatePatternRaw::FieldAbsent { lhs: lhs.id },
<lhs:Ident> <kind:BinCmpEq> <expr:ComplexExprImut> => PredicatePatternRaw::Bin { lhs: lhs.id, rhs: expr, kind },
}
TestExpr: TestExprRaw = {
<start:@L> <id:Ident> <tl:TestLiteral> <end:@L> => {
TestExprRaw { id: id.to_string(), test: tl, mid: NodeMeta::new_box(start, end)}
},
}
RecordPattern: RecordPatternRaw<'input> = {
<start:@L> "%{" <fields:PatternFields> "}" <end:@L> => RecordPatternRaw { fields: fields, mid: NodeMeta::new_box(start, end) },
<start:@L> "%{" "}" <end:@L> => RecordPatternRaw { fields: vec![], mid: NodeMeta::new_box(start, end) },
}
ArrayPattern: ArrayPatternRaw<'input> = {
<start:@L> "%[" <exprs:ArrayPredicatePatterns> "]" <end:@L> => ArrayPatternRaw { exprs, mid: NodeMeta::new_box(start, end) },
<start:@L> "%[" "]" <end:@L> => ArrayPatternRaw { exprs: vec![], mid: NodeMeta::new_box(start, end) },
}
TuplePattern: TuplePatternRaw<'input> = {
<start:@L> "%(" <exprs:TuplePredicatePatterns> <open:OpenTuple> ")" <end:@L> => TuplePatternRaw { exprs, mid: NodeMeta::new_box(start, end), open },
<start:@L> "%(" ")" <end:@L> => TuplePatternRaw { exprs: vec![], mid: NodeMeta::new_box(start, end), open: false },
<start:@L> "%(" "." "." "." ")" <end:@L> => TuplePatternRaw { exprs: vec![], mid: NodeMeta::new_box(start, end), open: true },
}
OpenTuple: bool = {
("," "." "." ".")? => <>.is_some()
}
TuplePredicatePatterns: ArrayPredicatePatternsRaw<'input> = {
<list:TuplePredicatePatterns> "," <item:TuplePredicatePattern> => {
let mut list = list;
list.push(item);
list
},
TuplePredicatePattern => vec![<>],
}
TuplePredicatePattern: ArrayPredicatePatternRaw<'input> = {
ArrayPredicatePattern => <>,
}
ArrayPredicatePattern: ArrayPredicatePatternRaw<'input> = {
"~" <test:TestExpr> => ArrayPredicatePatternRaw::Tilde(test),
"_" => ArrayPredicatePatternRaw::Ignore,
ComplexExprImut => ArrayPredicatePatternRaw::Expr(<>),
RecordPattern => ArrayPredicatePatternRaw::Record(<>),
}
ArrayPredicatePatterns: ArrayPredicatePatternsRaw<'input> = {
<list:ArrayPredicatePatterns> "," <item:ArrayPredicatePattern> => {
let mut list = list;
list.push(item);
list
},
ArrayPredicatePattern => vec![<>],
}
PatternFields: PatternFieldsRaw<'input> = {
<fields:PatternFields_> => {
let mut fields = fields;
fields.reverse();
fields
},
}
PatternFields_: PatternFieldsRaw<'input> = {
<fields:Sep<PatternFields_, PredicateFieldPattern, ",">> => fields,
}
Fields: FieldsRaw<'input> = {
<fields:Fields_> => {
let mut fields = fields;
fields.reverse();
fields
},
}
Fields_: FieldsRaw<'input> = {
<fields:Sep<Fields_, Field, ",">> => fields,
}
#[inline]
Ident: IdentRaw<'input> = {
<start:@L> <name:"<ident>"> <end:@L> => IdentRaw { id: name.0, mid: NodeMeta::new_box(start, end) },
}
#[inline]
TestLiteral: String = {
"<extractor>" => <>.join(""),
}
BytesLiteral: BytesRaw<'input> = {
<start:@L> "<<" ">>" <end:@L> => BytesRaw{ mid: NodeMeta::new_box(start, end), bytes: Vec::new()},
<start:@L> "<<" <bytes:Bytes> ">>" <end:@L> => BytesRaw{ mid: NodeMeta::new_box(start, end), bytes }
}
Bytes: Vec<BytesPartRaw<'input>> = {
<BytesPart> => vec![<>],
<bytes:Bytes> "," <b:BytesPart> => {
let mut bytes = bytes;
bytes.push(b);
bytes
}
}
BytesPart: BytesPartRaw<'input> = {
<start:@L> <data:SimpleExprImut> <end:@L> => BytesPartRaw{mid: NodeMeta::new_box(start, end), data, data_type: IdentRaw::none(NodeMeta::new_box(start, end)), bits: None},
<start:@L> <data:SimpleExprImut> ":" <bits:"int"> <end:@L> => BytesPartRaw{mid: NodeMeta::new_box(start, end), data, data_type: IdentRaw::none(NodeMeta::new_box(start, end)), bits: Some(bits)},
<start:@L> <data:SimpleExprImut> "/" <data_type:Ident> <end:@L> => BytesPartRaw{mid: NodeMeta::new_box(start, end), data, data_type, bits: None },
<start:@L> <data:SimpleExprImut> ":" <bits:"int"> "/" <data_type:Ident> <end:@L> => BytesPartRaw{mid: NodeMeta::new_box(start, end), data, bits: Some(bits), data_type},
}
Sep<L, T, D>: L = {
<item:T> D <list:L> => {
let mut list = list;
list.push(item);
list
},
<item:T> D? => vec![item],
}
/// Generic helper for binary operations
#[inline]
BinOp<Op, Current, Next>: ImutExprRaw<'input> = {
<start:@L> <lhs:(<Current>)> <op:(<Op>)> @L <rhs:Next> <end:@L> => ImutExprRaw::Binary(Box::new(BinExprRaw {
kind: op,
lhs: lhs,
rhs: rhs,
mid: NodeMeta::new_box(start, end)
})),
}
BinCmpEq: BinOpKind = {
BinEq => <>,
BinCmp => <>
}
BinOr: BinOpKind = {
"or" => BinOpKind::Or,
}
BinXor: BinOpKind = {
"xor" => BinOpKind::Xor,
}
BinAnd: BinOpKind = {
"and" => BinOpKind::And,
}
// BinBitOr: BinOpKind = {
// "|" => BinOpKind::BitOr,
// }
BinBitXor: BinOpKind = {
"^" => BinOpKind::BitXor,
}
BinBitAnd: BinOpKind = {
"&" => BinOpKind::BitAnd,
}
BinEq: BinOpKind = {
"==" => BinOpKind::Eq,
"!=" => BinOpKind::NotEq,
}
BinCmp: BinOpKind = {
">=" => BinOpKind::Gte,
">" => BinOpKind::Gt,
"<=" => BinOpKind::Lte,
"<" => BinOpKind::Lt,
}
BinBitShift: BinOpKind = {
">>" => BinOpKind::RBitShiftSigned,
">>>" => BinOpKind::RBitShiftUnsigned,
"<<" => BinOpKind::LBitShift,
}
BinAdd: BinOpKind = {
"+" => BinOpKind::Add,
"-" => BinOpKind::Sub,
}
BinMul: BinOpKind = {
"*" => BinOpKind::Mul,
"/" => BinOpKind::Div,
"%" => BinOpKind::Mod,
}
extern {
type Location = Location;
type Error = crate::errors::Error;
enum Token<'input> {
"<mod-comment>" => Token::ModComment(<&'input str>),
"<doc-comment>" => Token::DocComment(<&'input str>),
"<single-line-comment>" => Token::SingleLineComment(<&'input str>),
"let" => Token::Let,
"const" => Token::Const,
"match" => Token::Match,
"of" => Token::Of,
"end" => Token::End,
"case" => Token::Case,
"when" => Token::When,
"for" => Token::For,
"nil" => Token::Nil,
"and" => Token::And,
"or" => Token::Or,
"xor" => Token::Xor,
"not" => Token::Not,
"drop" => Token::Drop,
"emit" => Token::Emit,
"default" => Token::Default,
"patch" => Token::Patch,
"insert" => Token::Insert,
"upsert" => Token::Upsert,
"update" => Token::Update,
"erase" => Token::Erase,
"move" => Token::Move,
"copy" => Token::Copy,
"merge" => Token::Merge,
"event" => Token::Event,
"state" => Token::State,
"present" => Token::Present,
"absent" => Token::Absent,
"fn" => Token::Fun,
"intrinsic" => Token::Intrinsic,
"mod" => Token::Module,
"." => Token::Dot,
"\"" => Token::DQuote,
"\\#" => Token::EscapedHash,
";" => Token::Semi,
"::" => Token::ColonColon,
":" => Token::Colon,
"," => Token::Comma,
"=" => Token::Eq,
"_" => Token::DontCare,
"==" => Token::EqEq,
"!=" => Token::NotEq,
"#{" => Token::Interpol,
"{" => Token::LBrace,
"}" => Token::RBrace,
"(" => Token::LParen,
")" => Token::RParen,
"." => Token::Dot,
"$" => Token::Dollar,
"=>" => Token::EqArrow,
"[" => Token::LBracket,
"]" => Token::RBracket,
">=" => Token::Gte,
">" => Token::Gt,
"<=" => Token::Lte,
"<" => Token::Lt,
">>" => Token::RBitShiftSigned,
">>>" => Token::RBitShiftUnsigned,
"<<" => Token::LBitShift,
"!" => Token::BitNot,
// "|" => Token::BitOr,
"^" => Token::BitXor,
"&" => Token::BitAnd,
"+" => Token::Add,
"-" => Token::Sub,
"*" => Token::Mul,
"/" => Token::Div,
"%(" => Token::LPatParen,
"%[" => Token::LPatBracket,
"%{" => Token::LPatBrace,
"%" => Token::Mod,
"~=" => Token::TildeEq,
"~" => Token::Tilde,
"bool" => Token::BoolLiteral(<bool>),
"int" => Token::IntLiteral(<u64>),
"float" => Token::FloatLiteral(<f64>, <String>),
"string" => Token::StringLiteral(<Cow<'input, str>>),
"heredoc_start" => Token::HereDocStart,
"heredoc_end" => Token::HereDocEnd,
"heredoc" => Token::HereDocLiteral(<Cow<'input, str>>),
"<extractor>" => Token::TestLiteral(_, <Vec<String>>),
"<ident>" => Token::Ident(<Cow<'input, str>>, <bool>),
"<error>" => Token::Bad(<String>),
"<newline>" => Token::NewLine,
"select" => Token::Select,
"from" => Token::From,
"into" => Token::Into,
"create" => Token::Create,
"tumbling" => Token::Tumbling,
"sliding" => Token::Sliding,
"window" => Token::Window,
"stream" => Token::Stream,
"operator" => Token::Operator,
"where" => Token::Where,
"with" => Token::With,
"script" => Token::Script,
"having" => Token::Having,
"group" => Token::Group,
"by" => Token::By,
"define" => Token::Define,
"args" => Token::Args,
"recur" => Token::Recur,
"set" => Token::Set,
"each" => Token::Each,
"use" => Token::Use,
"as" => Token::As,
"pipeline" => Token::Pipeline,
"connector" => Token::Connector,
"flow" => Token::Flow,
"connect" => Token::Connect,
"to" => Token::To,
"deploy" => Token::Deploy,
"#!config" => Token::ConfigDirective,
}
}