use std::collections::HashMap;
use std::sync::Arc;
use std::sync::atomic::AtomicU8;
use parking_lot::Mutex;
use super::typeir::{CastIr, TypeIr};
pub type Reg = u16;
#[derive(Clone)]
pub enum Const {
Big(i128, super::numeric::IntWidth),
Float(f64),
F32(f32),
Char(char),
Str(Arc<str>),
Bytes(Arc<[u8]>),
}
pub const DISCARD: Reg = Reg::MAX;
#[derive(Clone, Copy, Debug)]
pub enum BinKind {
Add,
Sub,
Mul,
Div,
Rem,
Eq,
Ne,
Lt,
Le,
Gt,
Ge,
BitAnd,
BitOr,
BitXor,
Shl,
Shr,
}
pub fn overflow_message(op: BinKind) -> &'static str {
match op {
BinKind::Add => "attempt to add with overflow",
BinKind::Sub => "attempt to subtract with overflow",
BinKind::Mul => "attempt to multiply with overflow",
BinKind::Div => "attempt to divide with overflow",
BinKind::Rem => "attempt to calculate the remainder with overflow",
_ => "attempt to compute with overflow",
}
}
#[derive(Clone, Copy, Debug)]
pub enum UnKind {
Neg,
Not,
}
pub fn builtin_mutating(name: &str) -> bool {
matches!(
name,
"push"
| "pop"
| "insert"
| "insert_str"
| "remove"
| "swap_remove"
| "remove_entry"
| "push_str"
| "clear"
| "extend"
| "extend_from_slice"
| "append"
| "truncate"
| "retain"
| "retain_mut"
| "dedup"
| "dedup_by"
| "dedup_by_key"
| "sort"
| "sort_by"
| "sort_by_key"
| "sort_unstable"
| "sort_unstable_by"
| "sort_unstable_by_key"
| "reverse"
| "rotate_left"
| "rotate_right"
| "fill"
| "resize"
| "swap"
| "split_off"
| "drain"
| "iter_mut"
| "values_mut"
| "get_mut"
| "first_mut"
| "last_mut"
| "entry"
| "take"
| "replace"
| "get_or_insert"
| "get_or_insert_with"
| "make_ascii_uppercase"
| "make_ascii_lowercase"
| "clone_from"
| "copy_from_slice"
| "shuffle"
| "read_line"
| "read_to_string"
| "read_to_end"
)
}
#[derive(Clone)]
pub enum Member {
Named(Arc<str>),
Indexed(usize),
}
#[derive(Clone, Copy)]
pub enum CapSource {
Local(Reg),
Upvalue(u16),
MutableLocal(Reg),
MutableUpvalue(u16),
}
impl CapSource {
pub fn is_mutable(self) -> bool {
matches!(self, Self::MutableLocal(_) | Self::MutableUpvalue(_))
}
}
pub struct StructLit {
pub shape: Arc<StructShape>,
pub has_rest: bool,
}
#[derive(Clone)]
pub struct EnumVariant {
pub enum_name: Arc<str>,
pub variant: Arc<str>,
}
#[derive(Clone)]
pub struct MethodName {
pub text: String,
pub id: BuiltinId,
pub scalar: Option<ScalarTy>,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum ScalarTy {
Int(super::numeric::IntWidth),
F32,
F64,
Bool,
Char,
Str,
Opt(Box<ScalarTy>),
List(Box<ScalarTy>),
Map(Box<ScalarTy>),
Set(Box<ScalarTy>),
Other,
}
impl ScalarTy {
pub fn lower(ty: &syn::Type) -> Option<Self> {
let syn::Type::Path(path) = ty else {
return None;
};
Self::lower_segment(path.path.segments.last()?)
}
pub fn lower_segment(segment: &syn::PathSegment) -> Option<Self> {
if let syn::PathArguments::AngleBracketed(args) = &segment.arguments {
let inner = || Box::new(Self::first_arg(args).unwrap_or(Self::Other));
return match segment.ident.to_string().as_str() {
"Option" => Some(Self::Opt(inner())),
"Vec" | "VecDeque" => Some(Self::List(inner())),
"HashMap" | "BTreeMap" => Some(Self::Map(Box::new(
Self::nth_arg(args, 1).unwrap_or(Self::Other),
))),
"HashSet" | "BTreeSet" => Some(Self::Set(inner())),
_ => None,
};
}
Some(match segment.ident.to_string().as_str() {
"f32" => Self::F32,
"f64" => Self::F64,
"bool" => Self::Bool,
"char" => Self::Char,
"String" | "str" => Self::Str,
name => Self::Int(super::numeric::IntWidth::parse(name)?),
})
}
fn first_arg(args: &syn::AngleBracketedGenericArguments) -> Option<Self> {
Self::nth_arg(args, 0)
}
fn nth_arg(args: &syn::AngleBracketedGenericArguments, n: usize) -> Option<Self> {
args.args
.iter()
.filter_map(|arg| match arg {
syn::GenericArgument::Type(ty) => Some(ty),
_ => None,
})
.nth(n)
.and_then(Self::lower)
}
pub fn payload(&self) -> Option<&ScalarTy> {
match self {
Self::Opt(inner) | Self::List(inner) => Some(inner),
_ => None,
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum BuiltinId {
Len,
IsEmpty,
Clone,
ToString,
Get,
Insert,
ContainsKey,
Remove,
Entry,
Keys,
Values,
Iter,
IterMut,
Push,
Pop,
First,
Last,
SplitFirst,
Contains,
Sort,
Join,
Concat,
Sum,
Product,
Enumerate,
Rev,
Count,
Take,
Skip,
PushStr,
Then,
CloneFrom,
SplitWhitespace,
Split,
Chars,
Lines,
Trim,
StartsWith,
EndsWith,
Parse,
Unwrap,
UnwrapOr,
Copied,
Map,
Filter,
FilterMap,
FlatMap,
ForEach,
Find,
FindMap,
Position,
Any,
All,
Fold,
Reduce,
Retain,
SortByKey,
SortByCachedKey,
SortBy,
MaxByKey,
MinByKey,
TakeWhile,
SkipWhile,
Partition,
AndThen,
MapErr,
MapOr,
UnwrapOrElse,
OkOrElse,
WithContext,
OrInsertWith,
OrInsertWithKey,
AndModify,
Other,
}
impl BuiltinId {
pub fn resolve(name: &str) -> BuiltinId {
use BuiltinId::{
All, AndModify, AndThen, Any, Chars, Clone, CloneFrom, Concat, Contains, ContainsKey,
Copied, Count, EndsWith, Entry, Enumerate, Filter, FilterMap, Find, FindMap, First,
FlatMap, Fold, ForEach, Get, Insert, IsEmpty, Iter, IterMut, Join, Keys, Last, Len,
Lines, Map, MapErr, MapOr, MaxByKey, MinByKey, OkOrElse, OrInsertWith, OrInsertWithKey,
Other, Parse, Partition, Pop, Position, Product, Push, PushStr, Reduce, Remove, Retain,
Rev, Skip, SkipWhile, Sort, SortBy, SortByCachedKey, SortByKey, Split, SplitFirst,
SplitWhitespace, StartsWith, Sum, Take, TakeWhile, Then, ToString, Trim, Unwrap,
UnwrapOr, UnwrapOrElse, Values, WithContext,
};
match name {
"len" => Len,
"is_empty" => IsEmpty,
"clone" => Clone,
"to_string" => ToString,
"get" | "get_mut" => Get,
"then" => Then,
"clone_from" => CloneFrom,
"insert" => Insert,
"contains_key" => ContainsKey,
"remove" => Remove,
"entry" => Entry,
"keys" | "into_keys" => Keys,
"values" | "into_values" => Values,
"iter" | "into_iter" => Iter,
"iter_mut" => IterMut,
"push" => Push,
"pop" => Pop,
"first" | "first_mut" => First,
"last" | "last_mut" => Last,
"split_first" => SplitFirst,
"contains" => Contains,
"sort" | "sort_unstable" => Sort,
"join" => Join,
"concat" => Concat,
"sum" => Sum,
"product" => Product,
"enumerate" => Enumerate,
"rev" => Rev,
"count" => Count,
"take" => Take,
"skip" => Skip,
"push_str" => PushStr,
"split_whitespace" => SplitWhitespace,
"split" => Split,
"chars" => Chars,
"lines" => Lines,
"trim" => Trim,
"starts_with" => StartsWith,
"ends_with" => EndsWith,
"parse" => Parse,
"unwrap" => Unwrap,
"unwrap_or" => UnwrapOr,
"copied" | "cloned" => Copied,
"map" => Map,
"filter" => Filter,
"filter_map" => FilterMap,
"flat_map" => FlatMap,
"for_each" => ForEach,
"find" => Find,
"find_map" => FindMap,
"position" => Position,
"any" => Any,
"all" => All,
"fold" => Fold,
"reduce" => Reduce,
"retain" => Retain,
"sort_by_key" => SortByKey,
"sort_by_cached_key" => SortByCachedKey,
"sort_by" => SortBy,
"max_by_key" => MaxByKey,
"min_by_key" => MinByKey,
"take_while" => TakeWhile,
"skip_while" => SkipWhile,
"partition" => Partition,
"and_then" => AndThen,
"map_err" => MapErr,
"map_or" => MapOr,
"unwrap_or_else" => UnwrapOrElse,
"ok_or_else" => OkOrElse,
"with_context" => WithContext,
"or_insert_with" => OrInsertWith,
"or_insert_with_key" => OrInsertWithKey,
"and_modify" => AndModify,
_ => Other,
}
}
pub fn receivers(self) -> &'static [&'static str] {
use BuiltinId::{
AndThen, Chars, Clone, CloneFrom, Concat, Contains, ContainsKey, Copied, EndsWith,
Entry, Filter, First, Get, Insert, IsEmpty, Iter, IterMut, Join, Keys, Last, Len,
Lines, Map, MapOr, OkOrElse, Parse, Pop, Push, PushStr, Remove, Retain, Sort, SortBy,
SortByCachedKey, SortByKey, Split, SplitFirst, SplitWhitespace, StartsWith, Take,
ToString, Trim, Unwrap, UnwrapOr, UnwrapOrElse, Values,
};
match self {
Clone | ToString | CloneFrom => &["*"],
Len | IsEmpty | Get => &["Str", "Vec", "Map"],
Insert | Remove => &["Vec", "Map"],
ContainsKey | Entry | Keys | Values => &["Map"],
Iter => &["Vec", "Map", "Option"],
IterMut | Pop | First | Last | SplitFirst | Sort | SortByKey | SortByCachedKey
| SortBy | Join | Concat | Retain => &["Vec"],
Push | Contains => &["Str", "Vec"],
PushStr | SplitWhitespace | Split | Chars | Lines | Trim | StartsWith | EndsWith
| Parse => &["Str"],
Take | Unwrap | UnwrapOr | UnwrapOrElse | Copied | Map | Filter | AndThen | MapOr
| OkOrElse => &["Option"],
_ => &[],
}
}
pub fn is_higher_order(self) -> bool {
use BuiltinId::{
All, AndModify, AndThen, Any, Filter, FilterMap, Find, FindMap, FlatMap, Fold, ForEach,
Map, MapErr, MapOr, MaxByKey, MinByKey, OkOrElse, OrInsertWith, OrInsertWithKey, Other,
Partition, Position, Reduce, Retain, SkipWhile, SortBy, SortByCachedKey, SortByKey,
TakeWhile, Then, UnwrapOrElse, WithContext,
};
matches!(
self,
Then | Map
| Filter
| FilterMap
| FlatMap
| ForEach
| Find
| FindMap
| Position
| Any
| All
| Fold
| Reduce
| Retain
| SortByKey
| SortByCachedKey
| SortBy
| MaxByKey
| MinByKey
| TakeWhile
| SkipWhile
| Partition
| AndThen
| MapErr
| MapOr
| UnwrapOrElse
| OkOrElse
| WithContext
| OrInsertWith
| OrInsertWithKey
| AndModify
| Other
)
}
}
#[derive(Clone)]
pub struct FmtSpec {
pub template: String,
pub positional: Vec<Reg>,
pub named: Vec<(String, Reg)>,
}
pub struct PatInfo {
pub pat: PPat,
pub binds: Vec<(String, Reg)>,
}
#[derive(Clone)]
pub enum PLit {
Int(i64),
Float(f64),
Bool(bool),
Str(String),
Char(char),
}
#[derive(Clone)]
pub enum PPat {
Wild,
Rest,
Ident {
name: String,
sub: Option<Box<PPat>>,
},
Lit(PLit),
Tuple(Vec<PPat>),
TupleStruct {
name: Option<String>,
elems: Vec<PPat>,
},
Path {
name: Option<String>,
},
Struct {
name: Option<String>,
fields: Vec<(String, PPat)>,
},
Or(Vec<PPat>),
Slice(Vec<PPat>),
Range {
lo: Option<PLit>,
hi: Option<PLit>,
inclusive: bool,
},
Unsupported,
}
#[derive(Clone, Copy)]
pub enum MacroKind {
Println,
Print,
Eprintln,
Eprint,
Panic,
Anyhow,
Bail,
}
#[derive(Clone)]
pub enum Op {
LoadConst {
dst: Reg,
k: u16,
},
LoadInt {
dst: Reg,
v: i64,
},
LoadIntW {
dst: Reg,
v: i64,
w: super::numeric::IntWidth,
},
LoadBool {
dst: Reg,
v: bool,
},
LoadUnit {
dst: Reg,
},
LoadUpvalue {
dst: Reg,
idx: u16,
},
LoadCell {
dst: Reg,
cell: Reg,
},
StoreCell {
cell: Reg,
src: Reg,
},
DropCell {
cell: Reg,
},
StoreUpvalue {
idx: u16,
src: Reg,
},
LoadGlobal {
dst: Reg,
idx: u32,
},
Move {
dst: Reg,
src: Reg,
},
Bin {
dst: Reg,
a: Reg,
b: Reg,
op: BinKind,
},
BinImm {
dst: Reg,
a: Reg,
imm: i64,
op: BinKind,
},
Un {
dst: Reg,
a: Reg,
op: UnKind,
},
Jump {
to: u32,
},
LoopHead {
jump: u32,
},
JumpIfFalse {
cond: Reg,
to: u32,
},
JumpIfTrue {
cond: Reg,
to: u32,
},
CmpJump {
a: Reg,
b: Reg,
op: BinKind,
to: u32,
},
CmpJumpImm {
a: Reg,
imm: i64,
op: BinKind,
to: u32,
},
CallFn {
dst: Reg,
func: u32,
base: Reg,
argc: u16,
targ: u32,
},
CallValue {
dst: Reg,
callee: Reg,
base: Reg,
argc: u16,
},
CallPath {
dst: Reg,
path: u16,
base: Reg,
argc: u16,
},
PathValue {
dst: Reg,
path: u16,
},
Method {
dst: Reg,
recv: Reg,
name: u16,
base: Reg,
argc: u16,
},
GetOrDefault {
dst: Reg,
recv: Reg,
key: Reg,
default: Reg,
},
Ret {
src: Reg,
},
MakeVec {
dst: Reg,
base: Reg,
count: u16,
},
MakeTuple {
dst: Reg,
base: Reg,
count: u16,
},
MakeArrayRepeat {
dst: Reg,
val: Reg,
count: Reg,
},
MakeRange {
dst: Reg,
start: Reg,
end: Reg,
inclusive: bool,
},
IterInit {
dst: Reg,
src: Reg,
},
ForNext {
iter: Reg,
idx: Reg,
val: Reg,
to: u32,
},
MakeStruct {
dst: Reg,
info: u16,
base: Reg,
},
MakeEnum {
dst: Reg,
info: u16,
base: Reg,
count: u16,
},
LoadEnum {
dst: Reg,
info: u16,
},
MakeClosure {
dst: Reg,
child: u16,
},
Index {
dst: Reg,
base: Reg,
key: Reg,
},
SetIndex {
base: Reg,
key: Reg,
val: Reg,
},
Deref {
dst: Reg,
src: Reg,
},
SetDeref {
target: Reg,
val: Reg,
},
DerefBinAssign {
target: Reg,
val: Reg,
op: BinKind,
},
SetDerefParam {
target: Reg,
val: Reg,
},
GetField {
dst: Reg,
base: Reg,
member: u16,
},
SetField {
base: Reg,
member: u16,
val: Reg,
},
UniqueReg {
reg: Reg,
},
UniqueField {
dst: Reg,
base: Reg,
member: u16,
},
UniqueIndex {
dst: Reg,
base: Reg,
key: Reg,
},
UniqueCell {
dst: Reg,
cell: Reg,
},
UniqueUpvalue {
dst: Reg,
idx: u16,
},
RefIndex {
dst: Reg,
base: Reg,
key: Reg,
},
RefField {
dst: Reg,
base: Reg,
member: u16,
},
MakeBorrow {
dst: Reg,
src: Reg,
},
DefaultOf {
dst: Reg,
src: Reg,
},
DropScope {
list: u16,
},
MoveOut {
src: Reg,
},
Try {
dst: Reg,
src: Reg,
},
TryJump {
dst: Reg,
src: Reg,
to: u32,
},
Cast {
dst: Reg,
src: Reg,
ty: u16,
},
Coerce {
dst: Reg,
src: Reg,
ty: u16,
},
TestBind {
val: Reg,
pat: u16,
dst: Reg,
},
Fmt {
dst: Reg,
spec: u16,
},
MacroCall {
kind: MacroKind,
dst: Reg,
spec: u16,
},
Dbg {
dst: Reg,
base: Reg,
argc: u16,
},
Spawn {
dst: Reg,
child: u16,
},
Await {
dst: Reg,
src: Reg,
},
}
pub struct Chunk {
pub code: Vec<Op>,
pub lines: Vec<u32>,
pub file: Arc<str>,
pub num_regs: usize,
pub num_params: usize,
pub param_types: Vec<Option<String>>,
pub name: String,
pub module: u16,
pub consts: Vec<Const>,
pub members: Vec<Member>,
pub pats: Vec<PatInfo>,
pub fmts: Vec<FmtSpec>,
pub struct_lits: Vec<StructLit>,
pub enum_variants: Vec<EnumVariant>,
pub casts: Vec<CastIr>,
pub coerces: Vec<TypeIr>,
pub paths: Vec<(Vec<String>, Option<TypeIr>)>,
pub names: Vec<MethodName>,
pub children: Vec<Arc<Chunk>>,
pub child_caps: Vec<Vec<CapSource>>,
pub generics: Vec<Arc<str>>,
pub drop_lists: Vec<Arc<[Reg]>>,
pub call_type_args: Vec<Arc<[TypeIr]>>,
pub path_forwarder: bool,
pub loop_plans: Mutex<HashMap<usize, Option<Arc<super::scalar_loop::LoopPlan>>>>,
pub while_plans: Mutex<HashMap<usize, Arc<super::scalar_while::WhilePlan>>>,
pub while_rejected: Vec<AtomicU8>,
pub fn_plan: Mutex<Option<Arc<super::scalar_fn::FnPlan>>>,
pub fn_rejected: AtomicU8,
}
impl Chunk {
pub fn empty(name: impl Into<String>) -> Chunk {
Chunk {
code: Vec::new(),
lines: Vec::new(),
file: Arc::from(""),
num_regs: 0,
num_params: 0,
param_types: Vec::new(),
name: name.into(),
module: 0,
consts: Vec::new(),
members: Vec::new(),
pats: Vec::new(),
fmts: Vec::new(),
struct_lits: Vec::new(),
enum_variants: Vec::new(),
casts: Vec::new(),
coerces: Vec::new(),
paths: Vec::new(),
names: Vec::new(),
children: Vec::new(),
child_caps: Vec::new(),
generics: Vec::new(),
drop_lists: Vec::new(),
call_type_args: Vec::new(),
path_forwarder: false,
loop_plans: Mutex::new(HashMap::new()),
while_plans: Mutex::new(HashMap::new()),
while_rejected: Vec::new(),
fn_plan: Mutex::new(None),
fn_rejected: AtomicU8::new(0),
}
}
}
pub struct StructShape {
pub name: Arc<str>,
pub fields: Vec<Arc<str>>,
pub renames: Vec<Option<Arc<str>>>,
}
impl StructShape {
pub fn new(name: impl Into<Arc<str>>, fields: Vec<Arc<str>>) -> Arc<StructShape> {
Arc::new(StructShape {
name: name.into(),
fields,
renames: Vec::new(),
})
}
pub fn with_renames(
name: impl Into<Arc<str>>,
fields: Vec<Arc<str>>,
renames: Vec<Option<Arc<str>>>,
) -> Arc<StructShape> {
Arc::new(StructShape {
name: name.into(),
fields,
renames,
})
}
pub fn slot(&self, field: &str) -> Option<usize> {
self.fields.iter().position(|f| &**f == field)
}
}
pub fn path_call_chunk(segs: Vec<String>, num_params: usize) -> Arc<Chunk> {
let count = u16::try_from(num_params).expect("parameter count fits u16");
let dst = count * 2;
let mut chunk = Chunk::empty("<pathfn>");
chunk.path_forwarder = true;
chunk.num_params = num_params;
chunk.num_regs = num_params * 2 + 1;
chunk.paths.push((segs, None));
for i in 0..count {
chunk.code.push(Op::Move {
dst: count + i,
src: i,
});
}
chunk.code.push(Op::CallPath {
dst,
path: 0,
base: count,
argc: count,
});
chunk.code.push(Op::Ret { src: dst });
Arc::new(chunk)
}