use std::fmt;
use std::fmt::Write;
use parser::SrcLoc;
#[derive(Clone, Debug)]
pub struct Dumpster {
pub modules: Vec<Module>,
}
#[derive(Clone, Debug)]
pub enum ModuleKind {
Normal(Vec<NormalItem>),
}
#[derive(Clone, Debug)]
pub struct Module {
pub name: Ident,
pub data: ModuleKind,
pub loc: SrcLoc,
}
impl Module {
pub fn filename(&self) -> String {
let mut name = self.name.0.clone();
match self.data {
ModuleKind::Normal(_) => name.push_str(".bas"),
}
name
}
}
#[derive(Clone, Debug)]
pub enum NormalItem {
Function(FunDef),
Struct(StructDef),
Static(Static),
Const(Constant),
}
#[derive(Clone, Debug)]
pub struct FunDef {
pub name: Ident,
pub access: Access,
pub params: Vec<FunParam>,
pub optparams: Option<FunOptParams>,
pub ret: Type,
pub body: Vec<Stmt>,
pub loc: SrcLoc,
}
#[derive(Clone, Debug)]
pub struct FunParam {
pub name: Ident,
pub ty: Type,
pub mode: ParamMode,
pub loc: SrcLoc,
}
#[derive(Clone, Debug)]
pub enum FunOptParams {
VarArgs(Ident, SrcLoc),
Named(Vec<(FunParam, Literal)>),
}
impl FunOptParams {
pub fn max_len(&self) -> Option<usize> {
match *self {
FunOptParams::VarArgs(_, _) => None,
FunOptParams::Named(ref vec) => Some(vec.len()),
}
}
}
#[derive(Clone, Debug)]
pub struct Stmt {
pub data: StmtKind,
pub loc: SrcLoc,
}
#[derive(Clone, Debug)]
pub enum StmtKind {
ExprStmt(Expr),
VarDecl(Vec<(Ident, Type, Option<Expr>)>),
Assign(Expr, AssignOp, Expr),
Return(Option<Expr>),
IfStmt {
cond: Expr,
body: Vec<Stmt>,
elsifs: Vec<(Expr, Vec<Stmt>)>,
els: Option<Vec<Stmt>>,
},
WhileLoop {
cond: Expr,
body: Vec<Stmt>,
},
ForLoop {
var: (Ident, Type, ParamMode),
spec: ForSpec,
body: Vec<Stmt>,
},
ForAlong {
vars: Vec<Ident>,
along: Expr,
body: Vec<Stmt>,
},
Alloc(Expr, Vec<AllocExtent>),
ReAlloc(Expr, usize, AllocExtent),
DeAlloc(Expr),
Print(Vec<Expr>),
}
#[derive(Clone, Debug)]
pub enum ForSpec {
Range(Expr, Expr, Option<Expr>),
Each(Expr),
}
#[derive(Clone, Debug)]
pub enum AllocExtent {
Along(Expr),
Range(Option<Expr>, Expr),
}
#[derive(Clone, Debug)]
pub struct Expr {
pub data: ExprKind,
pub ty: Option<Type>,
pub loc: SrcLoc,
}
#[derive(Clone, Debug)]
pub enum ExprKind {
Lit(Literal),
Name(Path),
Index(Box<Expr>, Vec<Expr>),
Call(Path, Vec<Expr>, Vec<(Ident, Expr)>),
Member(Box<Expr>, Ident),
MemberInvoke(Box<Expr>, Ident, Vec<Expr>),
UnOpApp(Box<Expr>, UnOp),
BinOpApp(Box<Expr>, Box<Expr>, BinOp),
CondExpr {
cond: Box<Expr>,
if_expr: Box<Expr>,
else_expr: Box<Expr>,
},
ExtentExpr(Box<Expr>, ExtentKind, usize),
Cast(Box<Expr>, Type),
VbExpr(Vec<u8>),
}
impl Expr {
pub fn is_lvalue(&self) -> bool {
match self.data {
ExprKind::Name(_)
| ExprKind::Index(_, _)
| ExprKind::Member(_, _)
| ExprKind::VbExpr(_) => true,
_ => false,
}
}
}
#[derive(Copy, Clone, Debug)]
pub enum ExtentKind {
First,
Last,
Length,
}
#[derive(Clone, Debug)]
pub struct StructDef {
pub name: Ident,
pub access: Access,
pub members: Vec<StructMem>,
pub loc: SrcLoc,
}
#[derive(Clone, Debug)]
pub struct StructMem {
pub name: Ident,
pub ty: Type,
pub loc: SrcLoc,
}
#[derive(Clone, Debug)]
pub struct Static {
pub name: Ident,
pub access: Access,
pub ty: Type,
pub init: Option<Literal>,
pub loc: SrcLoc,
}
#[derive(Clone, Debug)]
pub struct Constant {
pub name: Ident,
pub access: Access,
pub ty: Type,
pub value: Literal,
pub loc: SrcLoc,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Ident(pub String, pub Option<String>);
impl fmt::Display for Ident {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.1.as_ref().unwrap_or(&self.0))?;
Ok(())
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Path(pub Option<Ident>, pub Ident);
impl fmt::Display for Path {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {
Some(ref module) => write!(f, "{}::{}", module, self.1),
None => write!(f, "{}", self.1),
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Access {
Private,
Public,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum ParamMode {
ByVal,
ByRef,
}
#[derive(Clone, Debug, PartialEq)]
pub enum Type {
Bool,
UInt8,
Int16,
Int32,
IntPtr,
Float32,
Float64,
String,
Currency,
Date,
Variant,
Obj,
Array(Box<Type>, ArrayBounds),
VarArgsArray,
Object(Path),
Struct(Path),
Enum(Path),
Deferred(Path),
Void,
}
impl Type {
pub fn is_object(&self) -> Option<bool> {
match *self {
Type::Obj | Type::Object(_) => Some(true),
Type::Variant | Type::Deferred(_) => None,
_ => Some(false),
}
}
pub fn is_scalar(&self) -> bool {
match *self {
Type::Array(_, _) | Type::Struct(_) | Type::Void => false,
_ => true,
}
}
pub fn might_be_numeric(&self) -> bool {
match *self {
Type::UInt8
| Type::Int16
| Type::Int32
| Type::IntPtr
| Type::Float32
| Type::Float64
| Type::Currency
| Type::Variant => true,
_ => false,
}
}
pub fn might_be_bitwise(&self) -> bool {
match *self {
Type::UInt8
| Type::Int16
| Type::Int32
| Type::IntPtr
| Type::Variant => true,
_ => false,
}
}
pub fn might_be_string(&self) -> bool {
match *self {
Type::String
| Type::Variant => true,
_ => false,
}
}
pub fn is_integral(&self) -> bool {
match *self {
Type::UInt8
| Type::Int16
| Type::Int32
| Type::IntPtr => true,
_ => false,
}
}
pub fn decay(&self) -> Type {
match *self {
Type::Array(ref base, ref bounds) =>
Type::Array(base.clone(), ArrayBounds::Dynamic(bounds.dims())),
ref ty => ty.clone(),
}
}
}
impl fmt::Display for Type {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Type::Bool => write!(f, "bool"),
Type::UInt8 => write!(f, "u8"),
Type::Int16 => write!(f, "i16"),
Type::Int32 => write!(f, "i32"),
Type::IntPtr => write!(f, "isize"),
Type::Float32 => write!(f, "f32"),
Type::Float64 => write!(f, "f64"),
Type::String => write!(f, "str"),
Type::Currency => write!(f, "currency"),
Type::Date => write!(f, "date"),
Type::Variant => write!(f, "var"),
Type::Obj => write!(f, "obj"),
Type::Object(ref path) => write!(f, "{}", path),
Type::Enum(ref path) => write!(f, "{}", path),
Type::Struct(ref path) => write!(f, "{}", path),
Type::Deferred(ref path) => write!(f, "{}", path),
Type::Array(ref base, ref bounds) =>
write!(f, "{}[{}]", base, bounds),
Type::VarArgsArray => write!(f, "..."),
Type::Void => write!(f, "void"),
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum StaticArrayDim {
Lit(Literal),
Named(Path),
}
impl fmt::Display for StaticArrayDim {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
StaticArrayDim::Lit(ref lit) => write!(f, "{}", lit),
StaticArrayDim::Named(ref path) => write!(f, "{}", path),
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum StaticArrayBound {
Range(StaticArrayDim, StaticArrayDim),
Length(StaticArrayDim),
}
impl fmt::Display for StaticArrayBound {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
StaticArrayBound::Range(ref first, ref end) =>
write!(f, "{}:{}", first, end),
StaticArrayBound::Length(ref len) =>
write!(f, "{}", len),
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum ArrayBounds {
Static(Vec<StaticArrayBound>),
Dynamic(usize),
}
impl ArrayBounds {
pub fn dims(&self) -> usize {
match *self {
ArrayBounds::Static(ref bounds) => bounds.len(),
ArrayBounds::Dynamic(dims) => dims,
}
}
}
impl fmt::Display for ArrayBounds {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ArrayBounds::Static(ref bounds) => {
for (i, ref bound) in bounds.iter().enumerate() {
if i != 0 {
f.write_str(", ")?;
}
bound.fmt(f)?;
}
Ok(())
},
ArrayBounds::Dynamic(dims) => {
for _ in 1..dims {
f.write_str(",")?;
}
Ok(())
},
}
}
}
#[derive(Clone, Copy, Debug)]
pub enum AssignOp {
Assign,
AddAssign,
SubAssign,
MulAssign,
DivAssign,
ModAssign,
PowAssign,
StrCatAssign,
BitAndAssign,
BitOrAssign,
LogAndAssign,
LogOrAssign,
}
#[derive(Clone, Copy, Debug)]
pub enum UnOp {
Negate,
BitNot,
LogNot,
AddressOf, }
#[derive(Clone, Copy, Debug)]
pub enum BinOp {
Add,
Sub,
Mul,
Div,
Mod,
Pow,
StrCat,
Eq,
NotEq,
IdentEq,
NotIdentEq,
Lt,
Gt,
LtEq,
GtEq,
BitAnd,
BitOr,
LogAnd,
LogOr,
}
#[derive(Clone, Debug, PartialEq)]
pub enum Literal {
NullPtr,
NullVar,
EmptyVar,
Bool(bool),
UInt8(u8),
Int16(i16),
Int32(i32),
IntPtr(i64),
Float32(f32),
Float64(f64),
String(String),
Currency(i64),
Date(f64),
}
impl Literal {
pub fn ty(&self) -> Type {
match *self {
Literal::NullPtr => Type::Obj,
Literal::NullVar => Type::Variant,
Literal::EmptyVar => Type::Variant,
Literal::Bool(_) => Type::Bool,
Literal::UInt8(_) => Type::UInt8,
Literal::Int16(_) => Type::Int16,
Literal::Int32(_) => Type::Int32,
Literal::IntPtr(_) => Type::IntPtr,
Literal::Float32(_) => Type::Float32,
Literal::Float64(_) => Type::Float64,
Literal::String(_) => Type::String,
Literal::Currency(_) => Type::Currency,
Literal::Date(_) => Type::Date,
}
}
pub fn num_of_type<T>(ty: &Type, val: T) -> Option<Self>
where T: Into<i64> + Into<f64> {
match *ty {
Type::UInt8 =>
Some(Literal::UInt8(<T as Into<i64>>::into(val) as u8)),
Type::Int16 =>
Some(Literal::Int16(<T as Into<i64>>::into(val) as i16)),
Type::Int32 =>
Some(Literal::Int32(<T as Into<i64>>::into(val) as i32)),
Type::IntPtr =>
Some(Literal::IntPtr(<T as Into<i64>>::into(val))),
Type::Float32 =>
Some(Literal::Float32(<T as Into<f64>>::into(val) as f32)),
Type::Float64 =>
Some(Literal::Float64(<T as Into<f64>>::into(val))),
_ => None,
}
}
}
impl fmt::Display for Literal {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Literal::NullPtr => f.write_str("nullptr"),
Literal::NullVar => f.write_str("nullvar"),
Literal::EmptyVar => f.write_str("emptyvar"),
Literal::Bool(b) => f.write_str(if b { "true" } else { "false" }),
Literal::UInt8(i) => write!(f, "{}u8", i),
Literal::Int16(i) => write!(f, "{}i16", i),
Literal::Int32(i) => write!(f, "{}i32", i),
Literal::IntPtr(i) => write!(f, "{}isize", i),
Literal::Float32(g) => write!(f, "{}f32", g),
Literal::Float64(g) => write!(f, "{}f64", g),
Literal::String(ref s) => unescape_string(&s, f),
Literal::Currency(c) =>
write!(f, "{}.{}currency", c / 10000, c % 10000),
Literal::Date(_) =>
panic!("dumpster fire: no format for literal dates yet"),
}
}
}
fn unescape_string(escaped: &str, f: &mut fmt::Formatter) -> fmt::Result {
for c in escaped.chars() {
match c {
'"' => f.write_str("\"\"")?,
'\t' => f.write_str("\\t")?,
'\n' => f.write_str("\\n")?,
c => f.write_char(c)?,
}
}
Ok(())
}