use crate::errors::SourceLocation;
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq)]
pub enum Type {
Integer,
Float,
String,
Boolean,
List(Box<Type>),
Map(Box<Type>), Buffer,
File,
Time,
Timer,
Value,
Void,
Unknown,
}
#[derive(Debug, Clone, PartialEq)]
pub enum FlagValueType {
Boolean,
Number,
Text,
}
#[derive(Debug, Clone, PartialEq)]
pub enum FileMode {
Reading,
Writing,
Appending,
}
#[derive(Debug, Clone)]
pub enum Expr {
IntegerLit(i64),
FloatLit(f64),
StringLit(String),
BoolLit(bool),
NothingLit,
Identifier(String),
BinaryOp {
left: Box<Expr>,
op: BinaryOperator,
right: Box<Expr>,
},
UnaryOp {
op: UnaryOperator,
operand: Box<Expr>,
},
Range {
start: Box<Expr>,
end: Box<Expr>,
inclusive: bool,
},
PropertyCheck {
value: Box<Expr>,
property: Property,
},
TypeCheck {
value: Box<Expr>,
type_noun: Type,
},
FunctionCall {
name: String,
args: Vec<Expr>,
},
ListLit {
elements: Vec<Expr>,
},
MapLit {
pairs: Vec<(Expr, Expr)>,
},
#[allow(dead_code)]
ListAccess {
list: Box<Expr>,
index: Box<Expr>,
},
PropertyAccess {
object: String,
property: ObjectProperty,
},
MapAccess {
map: String,
key: Box<Expr>,
},
#[allow(dead_code)]
LastError,
ArgumentCount,
ArgumentAt {
index: Box<Expr>,
},
ArgumentName, ArgumentFirst, ArgumentSecond, ArgumentLast, ArgumentEmpty, ArgumentAll, ArgumentRaw, ArgumentHas {
value: Box<Expr>,
},
TreatingAs {
value: Box<Expr>,
match_value: Box<Expr>,
replacement: Box<Expr>,
},
EnvironmentVariable {
name: Box<Expr>,
},
EnvironmentVariableCount,
EnvironmentVariableAt {
index: Box<Expr>,
},
EnvironmentVariableExists {
name: Box<Expr>,
},
EnvironmentVariableFirst, EnvironmentVariableLast, EnvironmentVariableEmpty,
CurrentTime, Fork, ReapChild { pid: Option<Box<Expr>>, },
Cast {
value: Box<Expr>,
target_type: Type,
radix: u32, },
DurationCast {
value: Box<Expr>,
unit: TimeUnit,
},
ByteAccess {
buffer: Box<Expr>,
index: Box<Expr>,
},
ElementAccess {
list: Box<Expr>,
index: Box<Expr>,
},
FormatString {
parts: Vec<FormatPart>,
},
FileAvailable {
path: Box<Expr>,
},
}
#[derive(Debug, Clone)]
pub enum FormatPart {
Literal(String),
Variable { name: String, format: Option<String> },
Expression { expr: Box<Expr>, format: Option<String> },
}
#[derive(Debug, Clone)]
pub enum TimeUnit {
Seconds,
Milliseconds,
}
#[derive(Debug, Clone)]
pub enum BinaryOperator {
Add, Subtract, Multiply, Divide, Modulo,
Equal, NotEqual, Greater, Less, GreaterEqual, LessEqual,
And, Or,
BitAnd, BitOr, BitXor, ShiftLeft, ShiftRight,
}
#[derive(Debug, Clone)]
pub enum UnaryOperator {
Negate,
Not,
}
#[derive(Debug, Clone)]
pub enum Property {
Even,
Odd,
Positive,
Negative,
Zero,
Empty,
}
#[derive(Debug, Clone)]
pub enum ObjectProperty {
Size, Capacity, Empty, Full,
Descriptor, Modified, Accessed, Permissions, Readable, Writable,
First, Last,
Keys, Values,
Absolute, Sign, Even, Odd, Positive, Negative, Zero,
Hour, Minute, Second, Day, Month, Year, Unix,
Duration, Elapsed, StartTime, EndTime, Running, }
#[derive(Debug, Clone)]
pub enum Statement {
Print {
value: Expr,
without_newline: bool,
},
VarDecl {
name: String,
var_type: Option<Type>,
value: Option<Expr>,
},
FlagSchemaDecl {
name: String,
short: String,
long: String,
value_type: FlagValueType,
required: bool,
default: Option<Expr>,
},
ParseFlags,
Assignment {
name: String,
value: Expr,
},
If {
condition: Expr,
then_block: Vec<Statement>,
else_if_blocks: Vec<(Expr, Vec<Statement>)>,
else_block: Option<Vec<Statement>>,
},
While {
condition: Expr,
body: Vec<Statement>,
},
ForRange {
variable: String,
range: Expr,
body: Vec<Statement>,
},
ForEach {
variable: String,
collection: Expr,
body: Vec<Statement>,
},
Repeat {
count: Expr,
body: Vec<Statement>,
},
Break,
Continue,
Exit {
code: Expr,
},
Return {
value: Option<Expr>,
declared_type: Option<Type>,
},
FunctionDef {
name: String,
params: Vec<(String, Type)>,
#[allow(dead_code)]
return_type: Type,
body: Vec<Statement>,
body_ended_early: Option<SourceLocation>,
},
FunctionCall {
name: String,
args: Vec<Expr>,
},
Allocate {
name: String,
size: Expr,
},
Free {
name: String,
},
Increment {
name: String,
},
Decrement {
name: String,
},
BufferDecl {
name: String,
size: Expr,
},
ByteSet {
buffer: String,
index: Expr,
value: Expr,
},
ElementSet {
list: String,
index: Expr,
value: Expr,
},
MapSet {
map: String,
key: Expr,
value: Expr,
},
ListAppend {
list: String,
value: Expr,
},
BufferCopy {
source: Expr,
destination: String,
},
BufferClear {
name: String,
},
FileOpen {
name: String,
path: Expr,
mode: FileMode,
},
FileRead {
source: String, buffer: String,
},
FileReadLine {
source: String, buffer: String,
},
FileSeekLine {
file: String,
line: Expr, },
FileSeekByte {
file: String,
byte: Expr, },
FileWrite {
file: String,
value: Expr,
},
FileWriteNewline {
file: String,
},
FileClose {
file: String,
},
FileDelete {
path: Expr,
},
Rmdir {
path: Expr,
},
OnError {
actions: Vec<Statement>,
},
BufferResize {
name: String,
new_size: Expr,
},
LibraryDecl {
name: String,
version: String,
},
See {
path: String,
lib_name: Option<String>,
lib_version: Option<String>,
},
TimerDecl {
name: String,
},
TimerStart {
name: String,
},
TimerStop {
name: String,
},
Wait {
duration: Expr,
unit: TimeUnit,
},
GetTime {
into: String,
},
Mkdir {
path: Expr,
},
Chdir {
path: Expr,
},
Symlink {
target: Expr,
linkpath: Expr,
},
Mknod {
path: Expr,
node_type: DeviceNodeType,
major: Expr,
minor: Expr,
},
Mount {
source: Expr,
target: Expr,
fstype: Expr,
options: Option<Expr>,
},
Unmount {
target: Expr,
lazy: bool,
},
Shutdown,
Reboot,
Halt,
PivotRoot {
new_root: Expr,
put_old: Expr,
},
Execute {
path: Expr,
args: Expr, },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DeviceNodeType {
Character, Block, Fifo, }
#[derive(Debug, Clone)]
pub struct Program {
pub statements: Vec<Statement>,
pub uses_heap: bool,
pub uses_strings: bool,
pub uses_io: bool,
pub uses_args: bool,
}
impl Program {
pub fn new(statements: Vec<Statement>) -> Self {
Program {
statements,
uses_heap: false,
uses_strings: false,
uses_io: false,
uses_args: false,
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum DefiniteDeclKind {
Plain,
Buffer,
List,
Map,
File,
}
pub fn collect_definite_decls(stmts: &[Statement]) -> std::collections::HashMap<String, DefiniteDeclKind> {
let mut out = std::collections::HashMap::new();
let mut poisoned: std::collections::HashSet<String> = std::collections::HashSet::new();
fn record(
out: &mut std::collections::HashMap<String, DefiniteDeclKind>,
poisoned: &mut std::collections::HashSet<String>,
name: &str,
kind: DefiniteDeclKind,
) {
if poisoned.contains(name) {
return;
}
match out.get(name) {
Some(existing) if *existing != kind => {
out.remove(name);
poisoned.insert(name.to_string());
}
_ => {
out.insert(name.to_string(), kind);
}
}
}
for stmt in stmts {
match stmt {
Statement::VarDecl { name, var_type, .. } => {
let kind = match var_type {
Some(Type::Buffer) => DefiniteDeclKind::Buffer,
Some(Type::List(_)) => DefiniteDeclKind::List,
Some(Type::Map(_)) => DefiniteDeclKind::Map,
_ => DefiniteDeclKind::Plain,
};
record(&mut out, &mut poisoned, name, kind);
}
Statement::BufferDecl { name, .. } => {
record(&mut out, &mut poisoned, name, DefiniteDeclKind::Buffer);
}
Statement::Allocate { name, .. } | Statement::TimerDecl { name } => {
record(&mut out, &mut poisoned, name, DefiniteDeclKind::Plain);
}
Statement::FileOpen { name, .. } => {
record(&mut out, &mut poisoned, name, DefiniteDeclKind::File);
}
Statement::GetTime { into } => {
record(&mut out, &mut poisoned, into, DefiniteDeclKind::Plain);
}
Statement::If { then_block, else_if_blocks, else_block: Some(else_block), .. } => {
let mut definite = collect_definite_decls(then_block);
for (_, block) in else_if_blocks {
let branch = collect_definite_decls(block);
definite.retain(|name, kind| branch.get(name) == Some(kind));
}
let branch = collect_definite_decls(else_block);
definite.retain(|name, kind| branch.get(name) == Some(kind));
for (name, kind) in definite {
record(&mut out, &mut poisoned, &name, kind);
}
}
_ => {}
}
}
out
}