selinux-cascade 0.0.2

A High Level Language for specifying SELinux policy
Documentation
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: MIT
use crate::ast::{CascadeString, Policy, Declaration, Expression, Statement, TypeDecl, FuncDecl, Argument, Annotation, LetBinding, Virtualable, FuncCall, DeclaredArgument};
use lalrpop_util::ErrorRecovery;

grammar<'err>(errors: &'err mut Vec<ErrorRecovery<usize, Token<'input>, &'static str>>);

// http://lalrpop.github.io/lalrpop/tutorial/006_macros.html
Comma<T>: Vec<T> = {
	<mut v:(<T> ",")*> <e:T?> => match e {
		None => v,
		Some(e) => {
			v.push(e);
			v
		}
	}
};

pub Policy: Box<Policy> = {
	Expr+ => Box::new(Policy::new(<>)),
}

Annotated<T>: T = {
	<a:Ann> <mut t:Annotated<T>> => {
		t.add_annotation(a);
		t
	},
	T
}

pub Expr: Expression = {
	Annotated<BaseExpr>,
	// On error, report and fast forward to the next expression
	! => { errors.push(<>); Expression::Error },
}

BaseExpr: Expression = {
	// TODO: other keywords beside virtual?
	<v: "virtual"?> <mut d: Decl> => {
		if v.is_some() {
			d.set_virtual();
		}
		Expression::Decl(d)
	},
	<Stmt> => Expression::Stmt(<>),
};

Decl: Declaration = {
	TypeDecl => Declaration::Type(<>),
	FuncDecl => Declaration::Func(<>),
}

TypeDecl: Box<TypeDecl> = {
	<dr:BuiltInType> <n:NameDecl> <i:InheritList?> "{" <mut v:Expr*> "}" => {
		let inherits = match i {
			None => vec![dr],
			Some(mut i) => {
				i.push(dr);
				i
			}
		};
		v.iter_mut().for_each(|e| e.set_class_name_if_decl(n.clone()));
		Box::new(TypeDecl::new(n, inherits, v))
	},
}

InheritList: Vec<CascadeString> = {
	"inherits" <Comma<Symbol>>,
}

BuiltInType: CascadeString = {
	<start: @L> <s: "domain"> <end: @R> => CascadeString::new(s.to_string(), start..end),
	<start: @L> <s: "resource"> <end: @R> => CascadeString::new(s.to_string(), start..end),
}

FuncDecl: Box<FuncDecl> = {
	"fn" <n: NameDecl> "(" <a: Comma<FuncDeclArg>> ")" "{" <b: Stmt*> "}" => Box::new(FuncDecl::new(n, a, b)),
}

FuncDeclArg: DeclaredArgument = {
	<t: Symbol> <n: NameDecl> <v: DefaultArg?> => DeclaredArgument { param_type: t, is_list_param: false, name: n, default: v },
	"[" <t: Symbol> "]" <n: NameDecl> <v: DefaultArg?> => DeclaredArgument { param_type: t, is_list_param: false, name: n, default: v },
}

#[inline]
DefaultArg: Argument = {
	"=" <Arg> => <>
}

Stmt: Statement = {
	<StmtBody> ";",
	IfBlock => Statement::IfBlock, // TODO
}

StmtBody: Statement = {
	<c:Symbol> "." <n:Symbol> "(" <a:Comma<Arg>> ")" => Statement::Call(Box::new(FuncCall::new(Some(c), n, a))),
	<n:Symbol> "(" <a:Comma<Arg>> ")" => Statement::Call(Box::new(FuncCall::new(None, n, a))),
	"let" <n:Symbol> "=" <a:Arg> => Statement::LetBinding(Box::new(LetBinding::new(n, a))),
}

Ann: Annotation = {
	"@" <s:NameDecl> "(" <a:Comma<Arg>> ")" => Annotation::new(s).set_arguments(a),
	"@" <s:NameDecl> => Annotation::new(s),
}

pub NameDecl: CascadeString = {
	// Naming rules:
	// * must start with a letter
	// * must not end with an underscore
	// * must not contain consecutive underscores
	// * can contain letters, digits and underscores
	<start: @L> <s: r"[a-zA-Z](_?([0-9a-zA-Z]+_)*[0-9a-zA-Z]+)?"> <end: @R>  => CascadeString::new(s.to_string(), start..end),
}

Symbol: CascadeString = {
	// Less constrained than NameDecl and accepts '-', which enables to use synthetic resources.
	<start: @L> <s: r"[a-zA-Z]+-[0-9a-zA-Z_-]*[0-9a-zA-Z]"> <end: @R>  => CascadeString::new(s.to_string(), start..end),
	NameDecl,
	BuiltInType
}

List: Vec<CascadeString> = {
	"[" <Symbol+> "]"
}

// TODO: Define boolean struct
BooleanExpr: () = {
	Symbol,
	BooleanExpr "&&" Symbol,
	BooleanExpr "||" Symbol,
	"(" BooleanExpr ")",
}

// TODO: Define if block struct
IfBlock: () = {
	"if" "(" BooleanExpr ")" "{" <Stmt+> "}" ElseBlock?,
}

#[inline]
ElseBlock: () = {
	"else" "{" <Stmt+> "}"
}

Arg: Argument = {
	Symbol => Argument::Var(<>),
	<s:Symbol> "=" <a: Arg> => Argument::Named(s, Box::new(a)),
	List => Argument::List(<>),
	Quoted_String => Argument::Quote(<>),
}

Quoted_String: CascadeString = {
	<start: @L> <s: r#""[^"]*""#> <end: @R> => CascadeString::new(s.to_string(), start..end),
}

// lexing precedence
match {
	r"\s*" => { },
	r"//[^\n\r]*[\n\r]*" => { },
} else {
	_
}