use crate::lexer::TokenKind;
use std::fmt;
use std::fmt::{Display, Formatter};
#[derive(Debug, Clone, PartialEq)]
pub struct IdentPart {
pub name: String,
pub array_indices: Vec<Vec<Option<Expr>>>,
}
impl IdentPart {
pub fn ident(name: impl Into<String>) -> Self {
IdentPart {
name: name.into(),
array_indices: Vec::new(),
}
}
}
impl Display for IdentPart {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name)?;
fmt_indices(f, &self.array_indices)?;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum IdentBase {
Partial(IdentPart),
Complete(IdentPart),
Me {
array_indices: Vec<Vec<Option<Expr>>>,
},
}
fn fmt_indices(f: &mut Formatter, array_indices: &Vec<Vec<Option<Expr>>>) -> fmt::Result {
for indices in array_indices {
write!(f, "(")?;
for (i, index) in indices.iter().enumerate() {
match index {
Some(index) => write!(f, "{index}")?,
None => write!(f, "")?,
}
if i < indices.len() - 1 {
write!(f, ", ")?;
}
}
write!(f, ")")?;
}
Ok(())
}
impl IdentBase {
pub fn ident(name: impl Into<String>) -> Self {
IdentBase::Complete(IdentPart::ident(name))
}
pub fn partial(name: impl Into<String>) -> Self {
IdentBase::Partial(IdentPart::ident(name))
}
pub fn me() -> Self {
IdentBase::Me {
array_indices: Vec::new(),
}
}
pub fn array_indices(&self) -> &Vec<Vec<Option<Expr>>> {
match &self {
IdentBase::Complete(part) => &part.array_indices,
IdentBase::Partial(part) => &part.array_indices,
IdentBase::Me { array_indices } => array_indices,
}
}
pub fn set_array_indices(&mut self, array_indices: Vec<Vec<Option<Expr>>>) {
match self {
IdentBase::Complete(part) => part.array_indices = array_indices,
IdentBase::Partial(part) => part.array_indices = array_indices,
IdentBase::Me {
array_indices: me_array_indices,
} => *me_array_indices = array_indices,
}
}
}
impl Display for IdentBase {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
IdentBase::Partial(part) => write!(f, ".{part}"),
IdentBase::Complete(part) => write!(f, "{part}"),
IdentBase::Me { array_indices } => {
write!(f, "Me")?;
fmt_indices(f, array_indices)?;
Ok(())
}
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct FullIdent(pub Box<Expr>);
impl FullIdent {
pub fn ident(name: impl Into<String>) -> Self {
FullIdent(Box::new(Expr::ident(name)))
}
pub fn new(expr: Expr) -> Self {
FullIdent(Box::new(expr))
}
}
impl Display for FullIdent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Expr {
Literal(Lit),
Ident(String),
IdentFnSubCall(FullIdent),
PrefixOp {
op: TokenKind,
expr: Box<Expr>,
},
InfixOp {
op: TokenKind,
lhs: Box<Expr>,
rhs: Box<Expr>,
},
New(String),
FnApplication {
callee: Box<Expr>,
args: Vec<Option<Expr>>,
},
WithScoped,
MemberExpression {
base: Box<Expr>,
property: String,
},
}
impl Expr {
pub fn ident2(name: impl Into<String>) -> Self {
Expr::IdentFnSubCall(FullIdent::ident(name))
}
pub fn ident(name: impl Into<String>) -> Self {
Expr::Ident(name.into())
}
pub fn int(i: isize) -> Self {
Expr::Literal(Lit::Int(i.to_string()))
}
pub fn int_str(i: impl Into<String>) -> Self {
Expr::Literal(Lit::Int(i.into()))
}
pub fn bool(b: bool) -> Self {
Expr::Literal(Lit::Bool(b))
}
pub fn str(s: impl Into<String>) -> Self {
Expr::Literal(Lit::Str(s.into()))
}
pub fn new(name: impl Into<String>) -> Self {
Expr::New(name.into())
}
pub fn member(base: Expr, property: impl Into<String>) -> Self {
Expr::MemberExpression {
base: Box::new(base),
property: property.into(),
}
}
pub fn fn_application(callee: Expr, args: Vec<Expr>) -> Self {
let args = args.into_iter().map(Some).collect();
Expr::FnApplication {
callee: Box::new(callee),
args,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Lit {
Int(String),
Float(f64),
Str(String),
Bool(bool),
DateTime(String),
Nothing,
Empty,
Null,
}
#[derive(Debug, Clone, PartialEq)]
pub enum ErrorClause {
ResumeNext,
Goto0,
}
#[derive(Debug, Clone, PartialEq)]
pub enum SetRhs {
Expr(Box<Expr>),
Nothing,
}
impl SetRhs {
pub fn ident(name: impl Into<String>) -> Self {
SetRhs::Expr(Box::new(Expr::ident(name)))
}
pub fn expr(expr: Expr) -> Self {
SetRhs::Expr(Box::new(expr))
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum DoLoopCondition {
While(Box<Expr>),
Until(Box<Expr>),
}
#[derive(Debug, Clone, PartialEq)]
pub enum DoLoopCheck {
Pre(DoLoopCondition),
Post(DoLoopCondition),
None,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Case {
pub tests: Vec<Expr>,
pub body: Vec<Stmt>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Stmt {
Dim {
vars: Vec<(String, Vec<Expr>)>,
},
ReDim {
preserve: bool,
var_bounds: Vec<(String, Vec<Expr>)>,
},
Const(Vec<(String, Lit)>),
Set {
var: FullIdent,
rhs: SetRhs,
},
Assignment {
full_ident: FullIdent,
value: Box<Expr>,
},
IfStmt {
condition: Box<Expr>,
body: Vec<Stmt>,
elseif_statements: Vec<(Box<Expr>, Vec<Stmt>)>,
else_stmt: Option<Vec<Stmt>>,
},
WhileStmt {
condition: Box<Expr>,
body: Vec<Stmt>,
},
ForStmt {
counter: String,
start: Box<Expr>,
end: Box<Expr>,
step: Option<Box<Expr>>,
body: Vec<Stmt>,
},
ForEachStmt {
element: String,
group: Box<Expr>,
body: Vec<Stmt>,
},
DoLoop {
check: DoLoopCheck,
body: Vec<Stmt>,
},
SelectCase {
test_expr: Box<Expr>,
cases: Vec<Case>,
else_stmt: Option<Vec<Stmt>>,
},
SubCall {
fn_name: FullIdent,
args: Vec<Option<Expr>>,
},
Call(FullIdent),
With {
object: FullIdent,
body: Vec<Stmt>,
},
Sub {
visibility: Visibility,
name: String,
parameters: Vec<Argument>,
body: Vec<Stmt>,
},
Function {
visibility: Visibility,
name: String,
parameters: Vec<Argument>,
body: Vec<Stmt>,
},
ExitDo,
ExitFor,
ExitFunction,
ExitProperty,
ExitSub,
OnError {
error_clause: ErrorClause,
},
}
#[derive(Debug, Clone, PartialEq)]
pub enum Argument {
ByVal(String),
ByRef(String),
}
#[derive(Debug, Clone, PartialEq)]
pub enum PropertyVisibility {
Public { default: bool },
Private,
}
#[derive(Debug, Clone, PartialEq)]
pub enum PropertyType {
Let,
Set,
Get,
}
#[derive(Debug, Clone, PartialEq)]
pub enum ArgumentType {
ByVal,
ByRef,
}
#[derive(Debug, Clone, PartialEq)]
pub struct MemberAccess {
pub visibility: PropertyVisibility,
pub name: String,
pub property_type: PropertyType,
pub args: Vec<(String, ArgumentType)>,
pub body: Vec<Stmt>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Visibility {
Default,
Public,
Private,
}
#[derive(Debug, Clone, PartialEq)]
pub struct MemberDefinitions {
pub visibility: Visibility,
pub properties: Vec<(String, Option<Vec<usize>>)>,
}
pub type ClassDim = Vec<(String, Option<Vec<usize>>)>;
#[derive(Debug, Clone, PartialEq)]
pub enum Item {
OptionExplicit,
Class {
name: String,
members: Vec<MemberDefinitions>,
dims: Vec<ClassDim>,
member_accessors: Vec<MemberAccess>,
methods: Vec<Stmt>, },
Const {
visibility: Visibility,
values: Vec<(String, Lit)>,
},
Variable {
visibility: Visibility,
vars: Vec<(String, Option<Vec<usize>>)>,
},
Statement(Stmt),
}
impl Stmt {
pub fn dim(var_name: impl Into<String>) -> Self {
Stmt::Dim {
vars: vec![(var_name.into(), Vec::new())],
}
}
pub fn const_(var_name: impl Into<String>, value: Lit) -> Self {
Stmt::Const(vec![(var_name.into(), value)])
}
pub fn assignment(ident: FullIdent, value: Expr) -> Self {
Stmt::Assignment {
full_ident: ident,
value: Box::new(value),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Type {
pub name: String,
pub generics: Vec<Type>,
}
impl fmt::Display for Expr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Expr::Literal(lit) => write!(f, "{lit}"),
Expr::Ident(ident) => write!(f, "{ident}"),
Expr::WithScoped => write!(f, "."),
Expr::IdentFnSubCall(ident) => {
write!(f, "{ident}")
}
Expr::PrefixOp { op, expr } => write!(f, "({op} {expr})"),
Expr::InfixOp { op, lhs, rhs } => write!(f, "({lhs} {op} {rhs})"),
Expr::New(name) => write!(f, "New {name}"),
Expr::FnApplication { callee, args } => {
write!(f, "{callee}(")?;
let len = args.len();
for (i, arg) in args.iter().enumerate() {
if let Some(arg) = arg {
write!(f, "{arg}")?;
}
if i != len - 1 {
write!(f, ", ")?;
}
}
write!(f, ")")
}
Expr::MemberExpression { base, property } => write!(f, "{base}.{property}"),
}
}
}
impl fmt::Display for Lit {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Lit::Int(i) => write!(f, "{i}"),
Lit::Float(fl) => write!(f, "{fl}"),
Lit::Str(s) => write!(f, r#""{}""#, s.replace('"', "\"\"")),
Lit::DateTime(dt) => write!(f, "#{dt}#"),
Lit::Bool(b) => {
if *b {
write!(f, "True")
} else {
write!(f, "False")
}
}
Lit::Nothing => write!(f, "Nothing"),
Lit::Empty => write!(f, "Empty"),
Lit::Null => write!(f, "Null"),
}
}
}
impl Lit {
pub fn str(s: impl Into<String>) -> Self {
Lit::Str(s.into())
}
pub fn int(i: isize) -> Self {
Lit::Int(i.to_string())
}
}