reset semanalyzer

This commit is contained in:
Logan Gatlin 2024-12-30 20:35:10 -06:00
parent fabb66148d
commit a783bcebb4
10 changed files with 35 additions and 1073 deletions

View file

@ -1,251 +0,0 @@
/*
use crate::{Expression, ExpressionKind, Statement, StatementKind};
use super::*;
impl Compiler {
pub fn generate(&mut self, statements: Vec<Statement>) {
for s in statements {
self.statement(s);
}
}
pub fn new_temporary(&mut self, type_: Type) -> UID {
let uid = format!("$$tmp{}", self.tmp_num);
self.push(IR::New {
uid: uid.clone(),
type_,
mutable: true,
global: false,
});
self.tmp_num += 1;
uid
}
pub fn statement(&mut self, stmt: Statement) {
use StatementKind as s;
match stmt.kind {
s::Declaration {
type_actual,
value,
mutable,
uid,
..
} => {
let global = self.table.table.get(&uid).unwrap().global;
match type_actual {
Type::Prim(_) | Type::Struct(_) => {
self.push(IR::New {
uid: uid.clone(),
type_: type_actual.clone(),
mutable,
global,
});
self.expression(value);
self.push(IR::Set {
uid,
type_: type_actual,
global,
});
},
Type::Nothing | Type::Function(_) | Type::Alias(_) => {
self.expression(value);
},
Type::Ambiguous => unreachable!(),
};
},
s::Assignment { name, value, uid } => {
let global = self.table.table.get(&uid).unwrap().global;
let type_ = value.type_.clone();
self.expression(value);
self.push(IR::Set { uid, type_, global });
},
s::If {
predicate,
block,
else_,
} => todo!(),
s::While { predicate, block } => todo!(),
s::Print(expression) => {
let type_ = expression.type_.clone();
self.expression(expression);
self.push(IR::Print { type_ });
},
s::Expression(expression) => {
let type_ = expression.type_.clone();
self.expression(expression);
self.push(IR::Drop { type_ });
},
s::Block(block) => {
for s in block {
self.statement(s);
}
},
s::Return(expression) => {
let type_ = if let Some(expression) = expression {
let type_ = expression.type_.clone();
self.expression(expression);
type_
} else {
Type::Nothing
};
self.push(IR::Return { type_ });
},
s::Error(diagnostic) => {
panic!("{diagnostic}")
},
}
}
pub fn expression(&mut self, expr: Expression) {
use ExpressionKind as e;
match expr.kind {
e::Immediate(immediate) => {
let Type::Prim(p) = expr.type_ else {
unreachable!();
};
self.push(IR::Push {
value: immediate,
prim: p,
})
},
e::Identifier(_, uid) => match expr.type_ {
Type::Prim(_) | Type::Struct(_) => self.push(IR::Get {
global: self.table.table.get(&uid).unwrap().global,
uid,
type_: expr.type_,
}),
Type::Ambiguous => unreachable!(),
_ => {},
},
e::Binary { op, left, right } => {
self.expression(*left);
self.expression(*right);
self.push(IR::BinOp {
op,
type_: expr.type_,
})
},
e::Unary { op, child } => {
self.expression(*child);
self.push(IR::UnOp {
op,
type_: expr.type_,
})
},
e::Parenthesis(inner) => self.expression(*inner),
e::FunctionDef {
params,
returns_actual,
body,
id,
..
} => {
self.push(IR::StartFunc {
uid: id,
params: params
.into_iter()
.map(|p| (p.name, p.type_actual))
.collect(),
returns: returns_actual,
});
for s in body {
self.statement(s);
}
self.push(IR::EndFunc);
},
e::FunctionCall {
callee, args, id, ..
} => {
self.expression(*callee);
for arg in args.into_iter().rev() {
self.expression(arg);
}
self.push(IR::Call { uid: id })
},
e::StructDef(..) => {},
e::StructLiteral { args, .. } => {
let struct_id = if let Type::Struct(s) = expr.type_ {
s
} else {
unreachable!()
};
let length = args.len();
let mut temp_buffer = vec![None; length];
let mut iter = args.into_iter();
let mut index = length - 1;
loop {
// If struct param has already been saved
if let Some((uid, type_)) = temp_buffer[index].take() {
self.ir.push(IR::Get {
uid,
type_,
global: false,
});
if index == 0 {
break;
}
index -= 1;
}
// If struct parameter has not been saved
else {
let (name, arg) = iter.next().unwrap();
let type_ = arg.type_.clone();
self.expression(arg);
let argno = self.table.get_field_no(struct_id, &name);
if argno != index {
let temp = self.new_temporary(type_.clone());
temp_buffer[argno] = Some((temp.clone(), type_.clone()));
self.push(IR::Set {
uid: temp,
type_: type_.clone(),
global: false,
});
} else {
if index == 0 {
break;
}
index -= 1;
}
}
}
},
e::Field {
namespace, field, ..
} => {
if let Type::Struct(sid) = namespace.type_ {
self.expression(*namespace);
let name = if let e::Identifier(name, _) = field.kind {
name
} else {
unreachable!()
};
// TODO extract field
let field_type = self.table.get_field(sid, &name).unwrap();
let temp = self.new_temporary(field_type.clone());
for (field_name, uid) in self.table.structs[sid].0.clone() {
let type_ =
self.table.resolve_type(&uid).unwrap().is_alias().unwrap();
if field_name != name {
self.push(IR::Drop { type_ });
} else {
self.push(IR::Set {
uid: temp.clone(),
type_: field_type.clone(),
global: false,
})
}
}
self.push(IR::Get {
uid: temp,
type_: field_type.clone(),
global: false,
});
} else {
self.expression(*namespace);
}
},
}
}
}
*/

View file

@ -1,198 +0,0 @@
/*
mod generate;
mod wasm;
use std::io::Write;
use crate::{
BinaryOp, Immediate, Statement, UnaryOp,
semantic::{Primitive, SymbolTable, Type, UID},
};
#[derive(Debug, Clone)]
pub enum IR {
BinOp {
op: BinaryOp,
type_: Type,
},
UnOp {
op: UnaryOp,
type_: Type,
},
Push {
prim: Primitive,
value: Immediate,
},
New {
uid: UID,
type_: Type,
mutable: bool,
global: bool,
},
Set {
uid: UID,
type_: Type,
global: bool,
},
Get {
uid: UID,
type_: Type,
global: bool,
},
StartFunc {
uid: UID,
params: Vec<(UID, Type)>,
returns: Type,
},
EndFunc,
Return {
type_: Type,
},
Call {
uid: UID,
},
Drop {
type_: Type,
},
Print {
type_: Type,
},
}
impl std::fmt::Display for IR {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use IR::*;
match self {
BinOp { op, type_ } => write!(f, "{op} ({type_})"),
UnOp { op, type_ } => write!(f, "{op}, {type_}"),
Push { prim, value } => write!(f, "push {value} ({prim})"),
New {
uid,
type_,
mutable,
..
} => write!(
f,
"let {}{uid} : {type_}",
if *mutable { "mut " } else { "" }
),
Set { uid, .. } => write!(f, "set local {uid}"),
Get { uid, .. } => write!(f, "get local {uid}"),
StartFunc {
uid,
params,
returns,
} => write!(f, "<function id={uid} params={params:?} returns={returns}>"),
EndFunc => write!(f, "</function>"),
Call { uid } => write!(f, "call {uid}"),
Return { type_ } => write!(f, "result {type_}"),
Print { type_ } => write!(f, "print {type_} [DEBUG]"),
Drop { .. } => write!(f, "pop"),
}
}
}
#[derive(Debug, Clone)]
pub struct Compiler {
ir: Vec<IR>,
table: SymbolTable,
tmp_num: usize,
}
impl Compiler {
pub fn new(table: SymbolTable) -> Self {
Self {
ir: vec![],
table,
tmp_num: 0,
}
}
pub fn compile(&mut self, statements: Vec<Statement>) {
self.generate(statements);
self.hoist();
for ir in &self.ir {
println!("{ir}");
}
let mut s = String::new();
for ir in &self.ir {
s.push_str(&self.ir_to_wat(ir.clone()).unwrap());
}
let assembly = format!("(module\n{s})");
println!("--------");
println!("{assembly}");
std::fs::File::create("test.wat")
.unwrap()
.write_all(assembly.as_bytes())
.unwrap();
let binary = wat::parse_str(assembly).unwrap();
std::fs::File::create("test.wasm")
.unwrap()
.write_all(&binary)
.unwrap();
}
pub fn push(&mut self, ir: IR) {
self.ir.push(ir);
}
fn hoist(&mut self) {
// Declarations, instructions
let mut functions = vec![(vec![], vec![])];
// Final IR output
let mut result = vec![];
for ir in &self.ir {
match ir {
IR::StartFunc { .. } => {
functions.push((vec![], vec![]));
},
IR::EndFunc => {
let (inits, instr) = functions.pop().unwrap();
for ir in inits {
result.push(ir);
}
for ir in instr {
result.push(ir);
}
result.push(IR::EndFunc);
continue;
},
_ => {},
}
// Push instruction to correct stack
let (inits, instr) = functions.last_mut().unwrap();
match ir {
IR::New { .. } | IR::StartFunc { .. } => {
inits.push(ir.clone());
},
_ => instr.push(ir.clone()),
}
}
// Initialize globals
let (inits, instr) = functions.pop().unwrap();
let mut main_locals = vec![];
for ir in inits {
match ir {
IR::New { global: true, .. } => {
result.push(ir);
},
_ => main_locals.push(ir),
}
}
// The main function (index 0)
result.push(IR::StartFunc {
uid: "$$main".into(),
params: vec![],
returns: Type::Nothing,
});
for ir in main_locals {
result.push(ir);
}
for ir in instr {
result.push(ir);
}
result.push(IR::EndFunc);
self.ir = result;
}
}
*/

View file

@ -1,213 +0,0 @@
/*
use crate::{
Base, BinaryOp, Immediate,
err::*,
semantic::{Primitive, Type},
};
use super::{Compiler, IR};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(non_camel_case_types)]
pub enum RegisterType {
f32,
f64,
i32,
i64,
}
impl std::fmt::Display for RegisterType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", match self {
RegisterType::f32 => "f32",
RegisterType::f64 => "f64",
RegisterType::i32 => "i32",
RegisterType::i64 => "i64",
})
}
}
const SYS_INT: RegisterType = RegisterType::i32;
const SYS_REAL: RegisterType = RegisterType::f32;
fn convert_sys_whole(input: &str, base: Base) -> Option<String> {
u32::from_str_radix(input, base as u32)
.ok()
.map(|i| format!("{i}"))
}
fn convert_sys_int(input: &str, base: Base) -> Option<String> {
i32::from_str_radix(input, base as u32)
.ok()
.map(|i| format!("{i}"))
}
fn convert_sys_real(input: &str) -> Option<String> {
input.parse::<f32>().ok().map(|f| format!("{f}"))
}
impl Compiler {
pub fn type_prim(&self, prim: Primitive) -> RegisterType {
use Primitive as p;
use RegisterType as r;
match prim {
p::i8 | p::i16 | p::i32 | p::w8 | p::w16 | p::w32 => r::i32,
p::i64 | p::w64 => r::i64,
p::integer | p::whole => SYS_INT,
p::r32 => r::f32,
p::r64 => r::f64,
p::real => SYS_REAL,
p::boolean => r::i32,
p::string => r::i64,
p::glyph => r::i32,
p::integer_ambiguous | p::real_ambiguous => unreachable!(),
}
}
pub fn splat(&self, type_: &Type) -> Vec<RegisterType> {
match type_ {
Type::Ambiguous => unreachable!(),
Type::Function(_) | Type::Nothing | Type::Alias(_) => vec![],
Type::Prim(prim) => vec![self.type_prim(*prim)],
Type::Struct(sid) => {
let struct_def = &self.table.structs[*sid].0;
let mut buf = vec![];
for (_, type_) in struct_def {
let type_ =
self.table.resolve_type(type_).unwrap().is_alias().unwrap();
buf.append(&mut self.splat(&type_));
}
buf
},
}
}
pub fn ir_to_wat(&self, ir: IR) -> Result<String> {
use Immediate as i;
use Primitive as p;
Ok(match ir {
IR::Push { value, prim } => match value {
i::Integer(ref i, base) => {
let b = base as u32;
// Unfortunately this can't be simplified
let s = match prim {
p::w8 => u8::from_str_radix(i, b).ok().map(|s| format!("{s}")),
p::w16 => u16::from_str_radix(i, b).ok().map(|s| format!("{s}")),
p::w32 => u32::from_str_radix(i, b).ok().map(|s| format!("{s}")),
p::w64 => u64::from_str_radix(i, b).ok().map(|s| format!("{s}")),
p::i8 => i8::from_str_radix(i, b).ok().map(|s| format!("{s}")),
p::i16 => i16::from_str_radix(i, b).ok().map(|s| format!("{s}")),
p::i32 => i32::from_str_radix(i, b).ok().map(|s| format!("{s}")),
p::i64 => i64::from_str_radix(i, b).ok().map(|s| format!("{s}")),
p::whole => convert_sys_whole(i, base),
p::integer => convert_sys_int(i, base),
_ => unreachable!(),
}
.reason(format!("Cannot parse immediate value as '{}'", prim))?;
format!("{}.const {}\n", self.type_prim(prim), s)
},
i::Real(ref i) => {
let s = match prim {
p::r32 => i.parse::<f32>().ok().map(|f| format!("{f}")),
p::r64 => i.parse::<f64>().ok().map(|f| format!("{f}")),
p::real => convert_sys_real(i),
_ => unreachable!(),
}
.reason(format!("Cannot parse immediate value as '{}'", prim))?;
format!("{}.const {}\n", self.type_prim(prim), s)
},
i::String(_) => todo!(),
i::Glyph(c) => format!("i32.const {}\n", c as u32),
i::Boolean(b) => format!("i32.const {}\n", b as i8),
},
IR::Drop { type_ } => {
let mut buffer = String::new();
for _ in 0..self.splat(&type_).len() {
buffer.push_str("drop\n");
}
buffer
},
IR::New {
uid,
type_,
mutable,
global,
} => {
let mut buffer = String::new();
for (index, rt) in self.splat(&type_).iter().enumerate() {
buffer.push_str(&format!(
"({} {uid}${index} {})\n",
if global { "global" } else { "local" },
if global && mutable {
format!("(mut {rt})")
} else {
format!("{rt}")
}
))
}
buffer
},
IR::Set { uid, type_, global } => {
let mut buffer = String::new();
for index in 0..self.splat(&type_).len() {
buffer.push_str(&format!(
"{}.set {uid}${index}\n",
if global { "global" } else { "local" }
))
}
buffer
},
IR::Get { uid, type_, global } => {
let mut buffer = String::new();
for index in (0..self.splat(&type_).len()).rev() {
buffer.push_str(&format!(
"{}.get {uid}${index}\n",
if global { "global" } else { "local" }
))
}
buffer
},
IR::StartFunc {
uid,
params,
returns,
} => {
let mut buffer = format!("(func {uid}\n");
for (puid, type_) in params {
for (id, rt) in self.splat(&type_).iter().enumerate() {
buffer.push_str(&format!("(param {puid}${id} {rt})\n"));
}
}
let returns = self.splat(&returns);
if returns.len() > 0 {
buffer.push_str("(result ");
for rt in returns {
buffer.push_str(&format!("{rt} "))
}
buffer.push_str(")\n");
}
buffer
},
IR::EndFunc => ")\n".into(),
IR::Return { type_ } => "return\n".into(),
IR::Call { uid } => format!("call {uid}\n"),
IR::BinOp { op, type_ } => {
use BinaryOp::*;
let p = if let Type::Prim(p) = type_ {
p
} else {
unreachable!()
};
match (op, p) {
(Plus, _) => format!("{}.add\n", self.type_prim(p)),
(Minus, _) => format!("{}.sub\n", self.type_prim(p)),
(Star, _) => format!("{}.mul\n", self.type_prim(p)),
_ => todo!(),
}
},
IR::UnOp { op, type_ } => todo!(),
IR::Print { type_ } => todo!(),
})
}
}
*/

View file

@ -1,6 +1,5 @@
#![feature(let_chains)]
mod err;
mod ir;
mod lookahead;
mod parse;
mod semantic;
@ -79,6 +78,6 @@ fn main() -> Result<()> {
*/
//let module = frontend::Module::from_file("./demo.hal")?;
//module.write_to("test");
test_expression("asdf~+ + 3");
test_expression("(a, b, c: d) {}");
Ok(())
}

View file

@ -1,75 +0,0 @@
use std::collections::HashMap;
use crate::{
Expression, ExpressionKind, Span, Statement, StatementKind, err::*,
};
use super::*;
use ir::*;
#[derive(Debug, Clone)]
enum Undo {
FuncGuard,
BlockGuard,
Symbol { name: String, prev: Symbol },
None,
}
#[derive(Debug, Clone)]
struct Symbol {
mangle: SID,
type_: TID,
life: Lifetime,
}
#[derive(Debug, Clone)]
struct SymbolTable {
type_table: Vec<Type>,
sym_table: HashMap<String, Symbol>,
undo_stack: Vec<Undo>,
path: Vec<String>,
salt: usize,
depth: usize,
}
impl SymbolTable {
fn define_symbol(&mut self, name: &str, type_: TID) {
let mut path = self.path.clone();
path.push(name.to_string());
let undo = match self.sym_table.get(name) {
Some(prev) => Undo::Symbol {
name: name.to_string(),
prev: prev.clone(),
},
None => Undo::None,
};
self.undo_stack.push(undo);
let mangle = names::mangle(path, &self.salt.to_string());
let symbol = Symbol {
mangle,
type_,
life: if self.depth == 0 {
Lifetime::Static
} else {
Lifetime::Dynamic
},
};
self.sym_table.insert(name.to_string(), symbol);
}
fn query_symbol(&mut self, name: &str) -> Result<&Symbol> {
self.sym_table.get(name).ok_or(Diagnostic::new(
format!("The name {name} is not declared in this scope",),
None,
))
}
fn define_type(&mut self, type_: Type) -> TID {
self.type_table.push(type_);
self.type_table.len()
}
}
pub fn analyze_block(stmts: Vec<Statement>) -> Result<Vec<IrNode>> {
todo!()
}

View file

@ -1,24 +0,0 @@
use super::{primitives::Primitive, SID};
pub fn mangle(input: &str) -> SID {
format!("$B${input}")
}
// Nothing ever happens
pub fn nothing() -> SID {
mangle("nothing")
}
pub fn integer() -> SID {
Primitive::integer_ambiguous.mangle()
}
pub fn real() -> SID {
Primitive::real_ambiguous.mangle()
}
pub fn all() -> Vec<SID> {
let mut uids = Primitive::ALL.map(|p| p.mangle()).to_vec();
uids.push(nothing());
uids
}

View file

@ -1,53 +0,0 @@
use crate::{BinaryOp, UnaryOp};
use super::{Type, SID, TID};
#[derive(Clone, Debug)]
pub struct IrBlock {
nodes: Vec<IrNode>,
}
#[derive(Clone, Debug)]
pub enum IrNode {
Declaration {
uid: SID,
mutable: bool,
size: usize,
value: IrExpr,
},
Function {
uid: SID,
parameters: Vec<Type>,
block: IrBlock,
},
Conditional {
branches: Vec<(IrExpr, IrBlock)>,
default: IrBlock,
},
Expr(IrExpr),
}
#[derive(Clone, Debug)]
pub struct IrExpr {
kind: IrExprKind,
type_: TID,
}
#[derive(Clone, Debug)]
pub enum IrExprKind {
Ident(SID),
UnOp {
op: UnaryOp,
child: Box<IrExpr>,
},
BinOp {
op: BinaryOp,
left: Box<IrExpr>,
right: Box<IrExpr>,
},
Block(IrBlock),
Call {
function: SID,
args: Vec<IrExpr>,
},
}

View file

@ -1,56 +1,46 @@
pub mod analyzer;
pub mod builtin;
pub mod ir;
pub mod names;
pub mod primitives;
pub type Mangle = String;
pub use primitives::*;
use crate::semantic::Primitive;
/// Type ID
pub type TID = usize;
/// Name mangle
pub type SID = String;
#[derive(Debug, Clone, Copy)]
pub enum Lifetime {
/// Exists for lifetime of program
Static,
/// Exists for lifetime of contained scope
Dynamic,
#[allow(non_camel_case_types)]
#[derive(Debug, Clone)]
pub enum Primitive {
i8,
i16,
i32,
i64,
integer,
integer_literal,
w8,
w16,
w32,
w64,
whole,
f32,
f64,
float,
float_literal,
glyph,
string,
boolean,
}
#[derive(Debug, Clone)]
pub enum Type {
/// Type is unknown at this stage
Ambiguous,
/// Zero size void type
None,
/// A primitive type
Prim(Primitive),
Nothing,
Never,
/// Constructed type
Struct {
size: usize,
name: String,
member_names: Vec<String>,
member_types: Vec<TID>,
field_names: Vec<String>,
field_types: Vec<Type>,
},
Alias(TID),
/// Non-capturing function type
Function {
name: String,
arg_names: Vec<String>,
arg_types: Vec<TID>,
field_types: Vec<Type>,
returns: Box<Type>,
},
}
impl std::fmt::Display for Type {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Type::Ambiguous => write!(f, "ambiguous"),
Type::Prim(primitive) => write!(f, "{primitive}"),
Type::Nothing => write!(f, "nothing"),
Type::Never => write!(f, "never"),
Type::Struct { name, .. } => write!(f, "struct {name}"),
Type::Alias(tid) => write!(f, "alias ({tid})"),
Type::Function { name, .. } => write!(f, "func ({name})"),
}
}
/// The name of a type
Type(Box<Type>),
}

View file

@ -1,16 +0,0 @@
// <mangle> ::= "$" <path> <salt>
// <path> ::= {<path-element>}*
// <path-element> ::= <length> <ident>
// <ident> ::= <_a-zA-Z> {<_a-zA-Z0-9>}*
// <length> ::= {<0-9>}+
// <salt> ::= {<a-zA-Z>}*
pub fn mangle(path: Vec<String>, salt: &str) -> String {
let mut buf: Vec<u8> = vec![];
for p in path {
let bytes = format!("{}{}", p.len(), punycode::encode(&p).unwrap());
buf.extend_from_slice(bytes.as_bytes());
}
buf.extend_from_slice(salt.as_bytes());
String::from_utf8(buf).unwrap()
}

View file

@ -1,197 +0,0 @@
use crate::{BinaryOp, UnaryOp};
use crate::err::*;
use crate::semantic::{builtin, SID};
macro_rules! count {
() => (0usize);
( $x:tt $($xs:tt)* ) => (1usize + count!($($xs)*));
}
macro_rules! primitives {
( $($i:ident),* ) => {
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[allow(non_camel_case_types, dead_code)]
pub enum Primitive {
integer_ambiguous,
real_ambiguous,
$($i,)*
}
impl Primitive {
pub const ALL: [Primitive; count!($($i)*,) - 1] = [$(Primitive::$i),*];
pub fn from_string(string: &str) -> Option<Self> {
match string {
$(stringify!{$i} => Some(Self::$i),)*
_ => None,
}
}
pub fn mangle(&self) -> SID {
match self {
Primitive::integer_ambiguous => builtin::mangle("integer_ambiguous"),
Primitive::real_ambiguous => builtin::mangle("real_ambiguous"),
$(
Primitive::$i => builtin::mangle(stringify!{$i}),
)*
}
}
}
impl std::fmt::Display for Primitive {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Primitive::integer_ambiguous => write!(f, "ambiguous integer"),
Primitive::real_ambiguous => write!(f, "ambiguous real"),
$(Primitive::$i => write!(f, stringify!{$i}),)*
}
}
}
};
}
primitives! {
w8, w16, w32, w64, whole,
i8, i16, i32, i64, integer,
r32, r64, real,
boolean,
string, glyph
}
impl Primitive {
pub fn as_wat(&self) -> &'static str {
use Primitive::*;
match self {
boolean | glyph | w8 | w16 | w32 | whole | i8 | i16 | i32 | integer => "i32",
w64 | i64 => "i64",
r32 | real => "f32",
r64 => "f64",
string => todo!(),
_ => panic!(),
}
}
}
macro_rules! selfsame_basic {
( $lhs:ident, $op:ident, $rhs:ident, $binop:ident, $i:ident ) => {
if ($i == $rhs && $rhs == $lhs) && $op == BinaryOp::$binop {
return Ok($i);
}
};
( $lhs:ident, $op:ident, $rhs:ident; $($i:ident),* ) => {
$(
selfsame_basic!($lhs, $op, $rhs, Plus, $i);
selfsame_basic!($lhs, $op, $rhs, Minus, $i);
selfsame_basic!($lhs, $op, $rhs, Star, $i);
selfsame_basic!($lhs, $op, $rhs, Slash, $i);
)*
logical!($lhs, $op, $rhs; $($i),*);
};
}
macro_rules! logical {
( $lhs:ident, $op:ident, $rhs:ident; $($i:ident),* ) => {
$(
selfsame_basic!($lhs, $op, $rhs, And, $i);
selfsame_basic!($lhs, $op, $rhs, Nand, $i);
selfsame_basic!($lhs, $op, $rhs, Xor, $i);
selfsame_basic!($lhs, $op, $rhs, Xnor, $i);
selfsame_basic!($lhs, $op, $rhs, Or, $i);
selfsame_basic!($lhs, $op, $rhs, Nor, $i);
)*
};
}
macro_rules! comparison {
( $lhs:ident, $op:ident, $rhs:ident, $binop:ident, $i:ident ) => {
if ($i == $rhs && $rhs == $lhs) && $op == BinaryOp::$binop {
return Ok(boolean);
}
};
( $lhs:ident, $op:ident, $rhs:ident; $($i:ident),* ) => {
$(
comparison!($lhs, $op, $rhs, DoubleEqual, $i);
comparison!($lhs, $op, $rhs, BangEqual, $i);
comparison!($lhs, $op, $rhs, Less, $i);
comparison!($lhs, $op, $rhs, LessEqual, $i);
comparison!($lhs, $op, $rhs, Greater, $i);
comparison!($lhs, $op, $rhs, GreaterEqual, $i);
)*
};
}
impl Primitive {
pub fn coerce(self, expect: Primitive) -> Result<Self> {
use Primitive::*;
match (self, expect) {
(integer_ambiguous, a @ (i8 | i16 | i32 | i64 | integer | w8 | w16 | w32 | w64 | whole))
| (a @ (i8 | i16 | i32 | i64 | integer | w8 | w16 | w32 | w64 | whole), integer_ambiguous) => {
Ok(a)
}
(real_ambiguous, a @ (r32 | r64 | real)) | (a @ (r32 | r64 | real), real_ambiguous) => Ok(a),
(t1, t2) if t1 == t2 => Ok(t1),
_ => error().reason(format!("Cannot coerce '{self}' into '{expect}'")),
}
}
pub fn is_ambiguous(&self) -> bool {
match self {
Primitive::integer_ambiguous | Primitive::real_ambiguous => true,
_ => false,
}
}
pub fn promote(&mut self) {
*self = match *self {
Primitive::integer_ambiguous => Primitive::integer,
Primitive::real_ambiguous => Primitive::real,
_ => *self,
}
}
pub fn binary_op(mut lhs: Primitive, op: BinaryOp, mut rhs: Primitive) -> Result<Primitive> {
use Primitive::*;
if lhs.is_ambiguous() && !rhs.is_ambiguous() {
lhs = lhs.coerce(rhs)?;
} else if rhs.is_ambiguous() && !lhs.is_ambiguous() {
rhs = rhs.coerce(lhs)?;
}
selfsame_basic! {
lhs, op, rhs; w8, w16, w32, w64, i8, i16, i32, i64,
integer, integer_ambiguous, real, real_ambiguous
}
logical! { lhs, op, rhs; boolean }
comparison! {
lhs, op, rhs; w8, w16, w32, w64, i8, i16, i32, i64,
integer, integer_ambiguous, real, real_ambiguous,
boolean, string
}
error().reason(format!(
"Binary {} is not defined for {} and {}",
op, lhs, rhs
))
}
pub fn unary_op(op: UnaryOp, child: Primitive) -> Result<Primitive> {
use Primitive::*;
use UnaryOp::*;
let e = error().reason(format!("Unary {} is not defined for {}", op, child));
match op {
Minus => match child {
boolean | string | glyph | whole | w8 | w16 | w32 | w64 => e,
_ => Ok(child),
},
Plus => match child {
boolean | string | glyph => e,
_ => Ok(child),
},
Not => match child {
string | glyph => e,
_ => Ok(child),
},
_ => error().reason(format!("Unary {} is not implemented (yet)", op)),
}
}
}