Skip to content

Instantly share code, notes, and snippets.

@ExpHP
Created February 6, 2021 21:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ExpHP/cb14c27a352a05dcc8b304d328188211 to your computer and use it in GitHub Desktop.
Save ExpHP/cb14c27a352a05dcc8b304d328188211 to your computer and use it in GitHub Desktop.
ast
use bstr::{BString};
use crate::meta;
use crate::var::{VarId};
use crate::ident::Ident;
use crate::pos::{Sp, Span};
use crate::error::CompileError;
use crate::type_system::{self, NameId};
// =============================================================================
/// Type used in the AST for the span of a single token with no useful data.
///
/// This can be used for things like keywords. We use [`Sp`]`<()>` instead of [`Span`]
/// because the latter would have an impact on equality tests.
pub type TokenSpan = Sp<()>;
/// Represents a complete script file.
#[derive(Debug, Clone, PartialEq)]
pub struct Script {
pub mapfiles: Vec<Sp<LitString>>,
pub image_sources: Vec<Sp<LitString>>,
pub items: Vec<Sp<Item>>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Item {
Func {
inline: Option<TokenSpan>,
keyword: Sp<FuncKeyword>,
name: Sp<Ident>,
params: Vec<(Sp<VarDeclKeyword>, Sp<Ident>)>,
/// `Some` for definitions, `None` for declarations.
code: Option<Block>,
},
AnmScript {
keyword: TokenSpan,
number: Option<Sp<i32>>,
name: Sp<Ident>,
code: Block,
},
Meta {
keyword: Sp<MetaKeyword>,
name: Option<Sp<Ident>>,
fields: Sp<meta::Fields>,
},
FileList {
keyword: Sp<FileListKeyword>,
files: Vec<Sp<LitString>>
},
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum FuncKeyword {
Type(FuncReturnType),
Sub,
Timeline,
}
string_enum!{
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum FuncReturnType {
#[str = "int"] Int,
#[str = "float"] Float,
#[str = "void"] Void,
}
}
string_enum!{
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum FileListKeyword {
#[str = "anim"] Anim,
#[str = "ecli"] Ecli,
}
}
string_enum! {
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum MetaKeyword {
/// `entry` block for a texture in ANM.
#[str = "entry"] Entry,
#[str = "meta"] Meta,
}
}
// =============================================================================
#[derive(Debug, Clone, PartialEq)]
pub struct Stmt {
pub time: i32,
pub body: StmtBody,
}
// FIXME: awkward now that `label:` is implemented as a statement instead
#[derive(Debug, Clone, PartialEq)]
pub enum StmtLabel {
Difficulty {
/// If `true`, the difficulty reverts to `"*"` after the next statement.
temporary: bool,
flags: DifficultyLabel,
},
}
/// Represents a statement, including the ';' if required, but
/// without any labels.
#[derive(Debug, Clone, PartialEq)]
pub enum StmtBody {
Jump(StmtGoto),
CondJump {
keyword: Sp<CondKeyword>,
cond: Sp<Cond>,
jump: StmtGoto,
},
Return {
keyword: TokenSpan,
value: Option<Sp<Expr>>,
},
CondChain(StmtCondChain),
Loop {
keyword: TokenSpan,
block: Block,
},
While {
while_keyword: TokenSpan,
do_keyword: Option<TokenSpan>,
cond: Sp<Cond>,
block: Block,
},
Times {
keyword: TokenSpan,
clobber: Option<Sp<Var>>,
count: Sp<Expr>,
block: Block,
},
/// Expression followed by a semicolon.
///
/// This is primarily for void-type "expressions" like raw instruction
/// calls (which are grammatically indistinguishable from value-returning
/// function calls), but may also represent a stack push in ECL.
Expr(Sp<Expr>),
Assignment {
var: Sp<Var>,
op: Sp<AssignOpKind>,
value: Sp<Expr>,
},
Declaration {
keyword: Sp<VarDeclKeyword>,
vars: Vec<Sp<(Sp<Var>, Option<Sp<Expr>>)>>,
},
/// An explicit subroutine call. (ECL only)
///
/// Will always have at least one of either the `@` or `async`.
/// (otherwise, it will fall under `Expr` instead)
CallSub {
at_symbol: bool,
async_: Option<CallAsyncKind>,
func: Sp<Ident>,
args: Vec<Sp<Expr>>,
},
/// An interrupt label: `interrupt[2]:`.
///
/// Because this compiles to an instruction, we store it as a statement in the AST rather than
/// as a label.
InterruptLabel(Sp<i32>),
/// A virtual instruction representing a label that can be jumped to.
Label(Sp<Ident>),
/// A virtual instruction that marks the end of a variable's lexical scope.
///
/// Blocks are eliminated during early compilation passes, leaving behind these as the only
/// remaining way of identifying the end of a variable's scope. They are used during lowering
/// to determine when to release resources (like registers) held by locals.
ScopeEnd(NameId),
/// A virtual instruction that completely disappears during compilation.
///
/// This is a trivial statement that doesn't even compile to a `nop();`.
/// It is inserted at the beginning and end of code blocks in order to help implement some
/// of the following things:
///
/// * A time label at the end of a block.
/// * A time label at the beginning of a loop's body.
///
/// Note that these may also appear in the middle of a block in the AST if a transformation
/// pass has e.g. inlined the contents of one block into another.
NoInstruction,
}
/// The body of a `goto` statement, without the `;`.
#[derive(Debug, Clone, PartialEq)]
pub struct StmtGoto {
pub destination: Sp<Ident>,
pub time: Option<i32>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct StmtCondChain {
// FIXME why do these have spans
pub cond_blocks: Vec<Sp<CondBlock>>,
pub else_block: Option<Block>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct CondBlock {
pub keyword: Sp<CondKeyword>,
pub cond: Sp<Cond>,
pub block: Block,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Cond {
Expr(Sp<Expr>),
/// This is how jmpDec works in register-based languages.
///
/// (stack-based ECL instead has a decrement operator that is postdec)
PreDecrement(Sp<Var>),
}
#[derive(Debug, Clone, PartialEq)]
pub enum CallAsyncKind {
CallAsync,
CallAsyncId(Box<Sp<Expr>>),
}
string_enum! {
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum CondKeyword {
#[str = "if"] If,
#[str = "unless"] Unless,
}
}
// TODO: Parse
pub type DifficultyLabel = BString;
string_enum! {
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum AssignOpKind {
#[str = "="] Assign,
#[str = "+="] Add,
#[str = "-="] Sub,
#[str = "*="] Mul,
#[str = "/="] Div,
#[str = "%="] Rem,
#[str = "|="] BitOr,
#[str = "^="] BitXor,
#[str = "&="] BitAnd,
}
}
/// A braced series of statements, typically written at an increased indentation level.
///
/// Every Block always has at least two [`Stmt`]s, as on creation it is bookended by dummy
/// statements to ensure it has a well-defined start and end time.
#[derive(Debug, Clone, PartialEq)]
pub struct Block(pub Vec<Sp<Stmt>>);
// =============================================================================
#[derive(Debug, Clone, PartialEq)]
pub enum Expr {
Ternary {
cond: Box<Sp<Expr>>,
question: Sp<()>,
left: Box<Sp<Expr>>,
colon: Sp<()>,
right: Box<Sp<Expr>>,
},
Binop(Box<Sp<Expr>>, Sp<BinopKind>, Box<Sp<Expr>>),
Call {
func: Sp<Ident>,
args: Vec<Sp<Expr>>,
},
Unop(Sp<UnopKind>, Box<Sp<Expr>>),
LitInt {
value: i32,
/// A hint to the formatter that it should use hexadecimal.
/// (may not necessarily represent the original radix of a parsed token)
hex: bool,
},
LitFloat { value: f32 },
LitString(LitString),
Var(Sp<Var>),
}
#[derive(Debug, Clone, PartialEq)]
pub enum Var {
/// A variable mentioned by name, possibly with a type sigil.
///
/// There should be none of these left in the AST after name resolution.
Named {
ty_sigil: Option<VarReadType>,
ident: Ident,
},
/// A resolved variable.
///
/// It can be created by performing [name resolution](crate::type_system::TypeSystem::resolve_names).
/// Additionally, variable accesses written in raw notation (e.g. `[10004.0]`)
/// parse directly to this form.
Resolved {
ty_sigil: Option<VarReadType>,
var_id: VarId,
},
}
string_enum! {
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum BinopKind {
#[str = "+"] Add,
#[str = "-"] Sub,
#[str = "*"] Mul,
#[str = "/"] Div,
#[str = "%"] Rem,
#[str = "=="] Eq,
#[str = "!="] Ne,
#[str = "<"] Lt,
#[str = "<="] Le,
#[str = ">"] Gt,
#[str = ">="] Ge,
#[str = "|"] BitOr,
#[str = "^"] BitXor,
#[str = "&"] BitAnd,
#[str = "||"] LogicOr,
#[str = "&&"] LogicAnd,
}
}
string_enum! {
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum UnopKind {
#[str = "!"] Not,
#[str = "-"] Neg,
#[str = "sin"] Sin,
#[str = "cos"] Cos,
#[str = "sqrt"] Sqrt,
#[str = "_S"] CastI,
#[str = "_f"] CastF,
}
}
// =============================================================================
string_enum! {
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum VarDeclKeyword {
#[str = "int"] Int,
#[str = "float"] Float,
#[str = "var"] Var,
}
}
/// The hinted type of a variable at a usage site, which can differ from its inherent type.
///
/// E.g. a variable's type may be hinted with the use of `$` or `%` prefixes.
/// (or it might not be hinted, meaning its type must be determined through other means)
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum VarReadType {
Int,
Float,
}
/// A literal string, which may be encoded in UTF-8 or Shift-JIS.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct LitString<S=BString> {
pub string: S,
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment