use crate::*;
use std::sync::atomic::Ordering;
use crate::transmute_ptr;
use crate::cmd_parse::{cmd_parse_argument_type, yystype_elif, cmd_parse_state, cmd_parse_command};
use crate::compat::queue::{tailq_concat, tailq_init, tailq_insert_head, tailq_remove, tailq_insert_tail, tailq_empty};
use crate::cmd_parse::lexer::Tok;
use crate::cmd_parse::lexer;
// scope, stack, input, commands
grammar<'a>(ps: NonNull<cmd_parse_state<'a>>);
pub Lines: Option<&'static mut cmd_parse_commands> = {
=> None,
<s:Statements> => Some(s),
};
pub Statements: &'static mut cmd_parse_commands = {
<s:Statement> "\n" => s,
<arg1:Statements> <arg2:Statement> "\n" => unsafe {
let value = arg1;
tailq_concat(value, arg2);
free_(arg2);
value
}
};
pub Statement: &'static mut cmd_parse_commands = {
=> unsafe { // Empty
let value: &'static mut cmd_parse_commands = Box::leak(Box::new(zeroed()));
tailq_init(value);
value
},
HiddenAssignment => unsafe {
let value = xcalloc1::<cmd_parse_commands>();
tailq_init(value);
value
},
<arg1:Condition> => unsafe {
if (*ps.as_ptr()).scope.as_ref().is_none_or(|scope| scope.flag) {
arg1
} else {
let value = cmd_parse_new_commands();
cmd_parse_free_commands(arg1);
value
}
},
<arg1:Commands> => unsafe {
if (*ps.as_ptr()).scope.as_ref().is_none_or(|scope| scope.flag) {
arg1
} else {
let value = cmd_parse_new_commands();
cmd_parse_free_commands(arg1);
value
}
}
};
pub Format: NonNull<u8> = {
<arg:format> => arg.unwrap(),
<arg:token> => arg.unwrap(),
};
pub Expanded: NonNull<u8> = {
<arg1:Format> => unsafe {
let pi = (*ps.as_ptr()).input.as_mut().map(|e|&raw mut *e).unwrap_or_default();
let c = (*pi).c;
let flags = format_flags::FORMAT_NOJOBS;
let mut fs = MaybeUninit::<cmd_find_state>::uninit();
let fsp = if cmd_find_valid_state(&raw const (*pi).fs) {
&(*pi).fs
} else {
cmd_find_from_client(fs.as_mut_ptr(), c, cmd_find_flags::empty());
fs.as_ptr()
};
let ft = format_create(null_mut(), (*pi).item, FORMAT_NONE, flags);
format_defaults(ft, c, NonNull::new((*fsp).s), NonNull::new((*fsp).wl), NonNull::new((*fsp).wp));
let value = format_expand(ft, arg1.as_ptr());
format_free(ft);
free_(arg1.as_ptr());
NonNull::new_unchecked(value)
}
};
pub OptionalAssignment: () = { Assignment? };
pub Assignment: () = {
<arg1:equals> => unsafe {
let flags = &(*ps.as_ptr()).input.as_ref().unwrap().flags;
if !flags.intersects(cmd_parse_input_flags::CMD_PARSE_PARSEONLY) && (*ps.as_ptr()).scope.as_ref().is_none_or(|scope| scope.flag) {
environ_put(GLOBAL_ENVIRON, transmute_ptr(arg1), environ_flags::empty());
}
free_(transmute_ptr(arg1));
}
};
pub HiddenAssignment: () = {
"%hidden" <arg2:equals> => unsafe {
let flags = &(*ps.as_ptr()).input.as_ref().unwrap().flags;
if !flags.intersects(cmd_parse_input_flags::CMD_PARSE_PARSEONLY) && (*ps.as_ptr()).scope.as_ref().is_none_or(|scope|scope .flag) {
environ_put(GLOBAL_ENVIRON, transmute_ptr(arg2), ENVIRON_HIDDEN);
}
free_(transmute_ptr(arg2));
}
};
pub IfOpen: bool = {
"%if" <arg2:Expanded> => unsafe {
let scope = xcalloc1::<cmd_parse_scope>();
let value = format_true(arg2.as_ptr());
scope.flag = value;
free_(arg2.as_ptr());
if let Some(ps_scope) = (*ps.as_ptr()).scope.as_mut() {
tailq_insert_head(&raw mut (*ps.as_ptr()).stack, *ps_scope);
}
(*ps.as_ptr()).scope = Some(scope);
value
}
};
pub IfElse: () = {
"%else" => unsafe {
let scope = xcalloc1::<cmd_parse_scope>();
scope.flag = !(*ps.as_ptr()).scope.as_ref().unwrap().flag;
free_((*ps.as_ptr()).scope.take().map(|p|p as *mut cmd_parse_scope).unwrap_or_default());
(*ps.as_ptr()).scope = Some(scope);
}
};
pub IfElif: bool = {
"%elif" <arg2:Expanded> => unsafe {
let scope = xcalloc1::<cmd_parse_scope>();
let value = format_true(arg2.as_ptr());
scope.flag = value;
free_(arg2.as_ptr());
free_((*ps.as_ptr()).scope.take().map(|p|p as *mut cmd_parse_scope).unwrap_or_default());
(*ps.as_ptr()).scope = Some(scope);
value
}
};
pub IfClose: () = {
"%endif" => unsafe {
free_((*ps.as_ptr()).scope.take().map(|p|p as *mut cmd_parse_scope).unwrap_or_default());
// TODO this breaks aliasing rules
let scope = tailq_first(&mut (*ps.as_ptr()).stack);
(*ps.as_ptr()).scope = if scope.is_null() { None } else { Some(&mut *scope) };
if let Some(ps_scope) = (*ps.as_ptr()).scope.as_mut() {
tailq_remove(&raw mut (*ps.as_ptr()).stack, *ps_scope);
}
}
};
pub Condition: &'static mut cmd_parse_commands = {
<arg1:IfOpen> "\n" <arg3:Statements> <arg4:IfClose> => unsafe {
if arg1 {
arg3
} else {
cmd_parse_free_commands(arg3);
cmd_parse_new_commands()
}
},
<arg1:IfOpen> "\n" <arg3:Statements> <arg4:IfElse> "\n" <arg6:Statements> <arg7:IfClose> => unsafe {
if arg1 {
cmd_parse_free_commands(arg6);
arg3
} else {
cmd_parse_free_commands(arg3);
arg6
}
},
<arg1:IfOpen> "\n" <arg3:Statements> <arg4:Elif> IfClose => unsafe {
if arg1 {
cmd_parse_free_commands(arg4.commands);
arg3
} else if arg4.flag != 0 {
cmd_parse_free_commands(arg3);
arg4.commands
} else {
cmd_parse_free_commands(arg3);
cmd_parse_free_commands(arg4.commands);
cmd_parse_new_commands()
}
},
<arg1:IfOpen> "\n" <arg3:Statements> <arg4:Elif> <arg5:IfElse> "\n" <arg7:Statements> <arg8:IfClose> => unsafe {
if arg1 {
cmd_parse_free_commands(arg4.commands);
cmd_parse_free_commands(arg7);
arg3
} else if arg4.flag != 0 {
cmd_parse_free_commands(arg3);
cmd_parse_free_commands(arg7);
arg4.commands
} else {
cmd_parse_free_commands(arg3);
cmd_parse_free_commands(arg4.commands);
arg7
}
}
};
pub Elif: yystype_elif = {
<arg1:IfElif> "\n" <arg3:Statements> => unsafe {
if arg1 {
yystype_elif {
flag : 1,
commands : arg3,
}
} else {
let tmp = yystype_elif {
flag : 0,
commands : cmd_parse_new_commands(),
};
cmd_parse_free_commands(arg3);
tmp
}
},
<arg1:IfElif> "\n" <arg3:Statements> <arg4:Elif> => unsafe {
if arg1 {
let tmp = yystype_elif {
flag : 1,
commands : arg3,
};
cmd_parse_free_commands(arg4.commands);
tmp
} else if arg4.flag != 0 {
let tmp = yystype_elif {
flag : 1,
commands : arg4.commands,
};
cmd_parse_free_commands(arg3);
tmp
} else {
let tmp = yystype_elif {
flag : 0,
commands : cmd_parse_new_commands(),
};
cmd_parse_free_commands(arg3);
cmd_parse_free_commands(arg4.commands);
tmp
}
}
};
pub Commands: &'static mut cmd_parse_commands = {
<arg1:Command> => unsafe {
let value = cmd_parse_new_commands();
if !tailq_empty(&raw mut arg1.arguments) && (*ps.as_ptr()).scope.as_ref().is_none_or(|scope| scope.flag) {
tailq_insert_tail(value, arg1);
} else {
cmd_parse_free_command(arg1);
}
value
},
<arg1:Commands> ";" => arg1,
<arg1:Commands> <_arg2:";"> <arg3:Condition1> => unsafe {
let value = arg1;
tailq_concat(value, arg3);
free_(arg3);
value
},
<arg1:Commands> <_arg2:";"> <arg3:Command> => unsafe {
let value;
if !tailq_empty(&raw mut arg3.arguments) && (*ps.as_ptr()).scope.as_ref().is_none_or(|scope| scope.flag) {
value = arg1;
tailq_insert_tail(value, arg3);
} else {
value = cmd_parse_new_commands();
cmd_parse_free_commands(arg1);
cmd_parse_free_command(arg3);
}
value
},
<arg1:Condition1> => arg1,
};
pub Command: &'static mut cmd_parse_command = {
Assignment => unsafe {
let command = xcalloc1::<cmd_parse_command>();
command.line = (*ps.as_ptr()).input.as_mut().unwrap().line.load(Ordering::SeqCst);
tailq_init(&raw mut command.arguments);
command
},
<_arg1:OptionalAssignment> <arg2:token> => unsafe {
let command = xcalloc1::<cmd_parse_command>();
command.line = (*ps.as_ptr()).input.as_mut().unwrap().line.load(Ordering::SeqCst);
tailq_init(&raw mut command.arguments);
let arg: *mut cmd_parse_argument = xcalloc1::<cmd_parse_argument>();
(*arg).type_ = cmd_parse_argument_type::String(transmute_ptr(arg2));
tailq_insert_head(&raw mut command.arguments, arg);
command
},
<_arg1:OptionalAssignment> <arg2:token> <arg3:Arguments> => unsafe {
let command = xcalloc1::<cmd_parse_command>();
command.line = (*ps.as_ptr()).input.as_mut().unwrap().line.load(Ordering::SeqCst);
tailq_init(&raw mut command.arguments);
tailq_concat(&raw mut command.arguments, arg3);
free_(arg3);
let arg: *mut cmd_parse_argument = xcalloc1::<cmd_parse_argument>();
(*arg).type_ = cmd_parse_argument_type::String(transmute_ptr(arg2));
tailq_insert_head(&raw mut command.arguments, arg);
command
},
};
pub Condition1: &'static mut cmd_parse_commands = {
<arg1:IfOpen> <arg2:Commands> <_arg3:IfClose> => unsafe {
if arg1 {
arg2
} else {
let value = cmd_parse_new_commands();
cmd_parse_free_commands(arg2);
value
}
},
<arg1:IfOpen> <arg2:Commands> <_arg3:IfElse> <arg4:Commands> <_arg5:IfClose> => unsafe {
if arg1 {
cmd_parse_free_commands(arg4);
arg2
} else {
cmd_parse_free_commands(arg2);
arg4
}
},
<arg1:IfOpen> <arg2:Commands> <arg3:Elif1> <arg4:IfClose> => unsafe {
if arg1 {
cmd_parse_free_commands(arg3.commands);
arg2
} else if arg3.flag != 0 {
cmd_parse_free_commands(arg2);
arg3.commands
} else {
let value = cmd_parse_new_commands();
cmd_parse_free_commands(arg2);
cmd_parse_free_commands(arg3.commands);
value
}
},
<arg1:IfOpen> <arg2:Commands> <arg3:Elif1> <arg4:IfElse> <arg5:Commands> <arg6:IfClose> => unsafe {
if arg1 {
let value = arg2;
cmd_parse_free_commands(arg3.commands);
cmd_parse_free_commands(arg5);
value
} else if arg3.flag != 0 {
let value = arg3.commands;
cmd_parse_free_commands(arg2);
cmd_parse_free_commands(arg5);
value
} else {
let value = arg5;
cmd_parse_free_commands(arg2);
cmd_parse_free_commands(arg3.commands);
value
}
}
};
pub Elif1: yystype_elif = {
<arg1:IfElif> <arg2:Commands> => unsafe {
if arg1 {
yystype_elif {
flag : 1,
commands : arg2,
}
} else {
let tmp = yystype_elif {
flag : 0,
commands : cmd_parse_new_commands(),
};
cmd_parse_free_commands(arg2);
tmp
}
},
<arg1:IfElif> <arg2:Commands> <arg3:Elif1> => unsafe {
if arg1 {
let tmp = yystype_elif {
flag : 1,
commands : arg2,
};
cmd_parse_free_commands(arg3.commands);
tmp
} else if arg3.flag != 0 {
let tmp = yystype_elif {
flag : 1,
commands : arg3.commands,
};
cmd_parse_free_commands(arg2);
tmp
} else {
let tmp = yystype_elif {
flag : 0,
commands : cmd_parse_new_commands(),
};
cmd_parse_free_commands(arg2);
cmd_parse_free_commands(arg3.commands);
tmp
}
}
}
pub Arguments: &'static mut cmd_parse_arguments = {
<arg1:Argument> => unsafe {
let value = xcalloc1::<cmd_parse_arguments>();
tailq_init(value);
tailq_insert_head(value, arg1);
value
},
<arg1:Argument> <arg2:Arguments> => unsafe {
tailq_insert_head(arg2, arg1);
arg2
},
};
pub Argument: &'static mut cmd_parse_argument = {
<token:token> => unsafe {
let value = xcalloc1::<cmd_parse_argument>();
value.type_ = cmd_parse_argument_type::String(transmute_ptr(token));
value
},
<equals:equals> => unsafe {
let value = xcalloc1::<cmd_parse_argument>();
value.type_ = cmd_parse_argument_type::String(transmute_ptr(equals));
value
},
"{" <statements:ArgumentStatements> => unsafe {
let value = xcalloc1::<cmd_parse_argument>();
value.type_ = cmd_parse_argument_type::Commands(statements);
value
}
};
pub ArgumentStatements: &'static mut cmd_parse_commands = {
<statement:Statement> "}" => statement,
<arg1:Statements> <arg2:Statement> "}" => unsafe {
let value = arg1;
tailq_concat(value, arg2);
free_(arg2);
value
}
};
// http://lalrpop.github.io/lalrpop/lexer_tutorial/003_writing_custom_lexer.html#updating-the-parser
extern {
type Location = usize;
type Error = crate::cmd_parse::lexer::LexicalError;
// pub enum Tok {
// Error,
// Hidden,
// // If,
// // Else,
// // Elif,
// // Endif,
// Format(*const c_char),
// Token(*const c_char),
// Equals(*const c_char),
// }
enum crate::cmd_parse::lexer::Tok {
"\n" => lexer::Tok::Newline,
";" => lexer::Tok::Semicolon,
"{" => lexer::Tok::LeftBrace,
"}" => lexer::Tok::RightBrace,
"%hidden" => lexer::Tok::Hidden,
"%if" => lexer::Tok::If,
"%elif" => lexer::Tok::Elif,
"%endif" => lexer::Tok::Endif,
"%else" => lexer::Tok::Else,
error => lexer::Tok::Error,
format => lexer::Tok::Format(<Option<NonNull<u8>>>),
token => lexer::Tok::Token(<Option<NonNull<u8>>>),
equals => lexer::Tok::Equals(<Option<NonNull<u8>>>),
}
}
// the C lexer seems to pair braces in the format string
// maybe better to not do that at the lexer level?
// pub FORMAT: NonNull<c_char> = r"#\{" => NonNull::new((<>).to_string().leak().as_mut_ptr().cast()).unwrap();
// pub EQUALS: NonNull<c_char> = r"=[^\n]*" => NonNull::new((<>).to_string().leak().as_mut_ptr().cast()).unwrap();
// pub TOKEN: NonNull<c_char> = r"[^=#].*" => NonNull::new((<>).to_string().leak().as_mut_ptr().cast()).unwrap();
// equals seems to consume everything after =
// match {
// r"\s*" => { }, // The default whitespace skipping is disabled if an `ignore pattern` is specified
// r"#[^{][^\n\r]*[\n\r]*" => { }, // # followed by anything other then { is a comment
// }