PrimitiveType Lint (#1845)

Adds a line transformer to `sotn-lint` for replacing integers with the
appropriate `PrimitiveType` enum when the variable is named `prim` and
the field being accessed or mutated is named `type`.

The regex for matching statements with enum fields and some binary
operation has been moved out to be shared by `EnumLineTransformer` which
can find and replace traditional incrementing `enum`s, as well as the
existing `BitFlagLingTransformer`, which finds and replaces flag-type
enums.
This commit is contained in:
Jonathan Hohle 2024-10-29 15:24:36 -07:00 committed by GitHub
parent 7d285b6d5e
commit 7b8ccc3e29
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 264 additions and 39 deletions

View File

@ -1681,7 +1681,7 @@ s32 func_800EDB58(u8 primType, s32 count) {
isLooping = 1;
while (isLooping) {
var_v1--;
if (prim->type != 0) {
if (prim->type != PRIM_NONE) {
var_v1 = i;
primStartIdx = var_v1 + 1;
var_v1 = count;
@ -1760,7 +1760,7 @@ s32 func_800EDD9C(u8 type, s32 count) {
i = LEN(g_PrimBuf) - 1;
while (i >= 0) {
if (prim->type == 0) {
if (prim->type == PRIM_NONE) {
DestroyPrimitive(prim);
if (count == 1) {
prim->type = type;

View File

@ -842,7 +842,7 @@ void func_80104790(s32 arg0, s32 arg1, s32 arg2) {
(s32*)&prim->x1, &sp78, &sp7C, &sp80);
}
func_801072DC(prim);
prim->type = 5;
prim->type = PRIM_GT3;
if (sp7C >= 0xF0) {
continue;
}
@ -953,7 +953,7 @@ void func_80105078(s32 arg0, s32 arg1) {
RotAverage3(&sp40[0], &sp40[2], &sp40[1], (s32*)&prim->x0,
(s32*)&prim->x2, (s32*)&prim->x1, &sp78, &sp80);
}
prim->type = 2;
prim->type = PRIM_LINE_G2;
if (sp7C < 0xF0) {
if (temp_v0_4 >= 0) {
prim->priority = g_unkGraphicsStruct.g_zEntityCenter + 3;

View File

@ -804,7 +804,7 @@ void EntityHitByIce(Entity* self) {
prim->drawMode = DRAW_UNK_200 | DRAW_UNK_100 | DRAW_TPAGE |
DRAW_COLORS | DRAW_TRANSP;
}
prim->type = 3;
prim->type = PRIM_G4;
prim->priority = PLAYER.zPriority + 2;
prim = prim->next;
}

View File

@ -40,13 +40,13 @@ void EntityTeleport(Entity* self) {
prim->y0 = 0;
prim->u0 = 0xC0;
prim->v0 = 0xF0;
prim->type = 1;
prim->type = PRIM_TILE;
prim->priority = 0x1FD;
prim->drawMode = DRAW_TPAGE2 | DRAW_TPAGE | DRAW_HIDE | DRAW_TRANSP;
prim = prim->next;
}
for (i = 0; i < 2; i++) {
prim->type = 3;
prim->type = PRIM_G4;
prim->priority = 0x1F8;
prim->drawMode = DRAW_TPAGE2 | DRAW_TPAGE | DRAW_TRANSP;
prim = prim->next;
@ -863,7 +863,7 @@ void EntityHolyWaterBreakGlass(Entity* self) {
}
fakeprim->velocityY.val = -((rand() * 2) + FIX(2.5));
fakeprim->drawMode = DRAW_HIDE | DRAW_UNK02;
fakeprim->type = 1;
fakeprim->type = PRIM_TILE;
} else {
prim->r0 = prim->r1 = prim->r2 = prim->r3 =
(rand() & 0xF) | 0x30;
@ -885,7 +885,7 @@ void EntityHolyWaterBreakGlass(Entity* self) {
prim->y1 = posY + D_800B0658[arrIndex][3];
prim->x3 = prim->x2 = posX + D_800B0658[arrIndex][4];
prim->y3 = prim->y2 = posY + D_800B0658[arrIndex][5];
prim->type = 3;
prim->type = PRIM_G4;
prim->priority = PLAYER.zPriority + 2;
}
}
@ -2166,7 +2166,7 @@ void EntitySubwpnAgunea(Entity* self) {
PLAYER.posY.i.hi + PLAYER.hitboxOffY - 8;
self->posX.i.hi = self->ext.agunea.unk80 = PLAYER.posX.i.hi;
prim = &g_PrimBuf[self->primIndex];
prim->type = 2;
prim->type = PRIM_LINE_G2;
prim->priority = PLAYER.zPriority + 2;
prim->drawMode = DRAW_UNK_200 | DRAW_UNK_100 | DRAW_TPAGE2 |
DRAW_TPAGE | DRAW_TRANSP;
@ -2510,7 +2510,7 @@ void func_80129864(Entity* self) {
}
prim = &g_PrimBuf[self->primIndex];
for (i = 0; i < 8; i++) {
prim->type = 1;
prim->type = PRIM_TILE;
prim->u0 = 1;
prim->v0 = 1;
prim->drawMode = DRAW_UNK02;
@ -2755,7 +2755,7 @@ void EntitySummonSpirit(Entity* self) {
selfY = self->posY.i.hi;
prim = &g_PrimBuf[self->primIndex];
prim->type = 3;
prim->type = PRIM_G4;
prim->drawMode = DRAW_UNK_100 | DRAW_UNK02;
prim->x2 = prim->x0 = selfX - 0x20;
prim->x3 = prim->x1 = selfX + 0x1F;

View File

@ -644,7 +644,7 @@ void EntityBatEcho(Entity* self) {
prim = (Primitive*)&g_PrimBuf[self->primIndex];
self->ext.batEcho.unk84 = prim;
for (i = 0; i < 0x11; i++) {
prim->type = 1;
prim->type = PRIM_TILE;
prim->drawMode = DRAW_HIDE;
prim->y2 = D_800B0884[i];
prim->x2 = (i * 3) + 1;
@ -765,7 +765,7 @@ void EntityBatEcho(Entity* self) {
prim->b1 = temp->b1 =
(-unkstruct->unk10 + 0x41) * unkstruct->unkC * temp_unk7E / FIX(1);
prim->type = 3;
prim->type = PRIM_G4;
prim = prim->next;
*(s32*)&prim->x0 = *(s32*)&temp->x0;
*(s32*)&prim->x1 = *(s32*)&temp->x1;
@ -776,7 +776,7 @@ void EntityBatEcho(Entity* self) {
prim->y2 = var_s6;
var_s6 = temp->y1 + unkstruct->unk8;
prim->y3 = var_s6;
prim->type = 3;
prim->type = PRIM_G4;
prim = prim->next;
}
}

View File

@ -2184,7 +2184,7 @@ void EntityGiantSpinningCross(Entity* self) {
gte_ldv3(vectors_ptr[0], vectors_ptr[1], vectors_ptr[3]);
gte_rtpt();
temp_a3 = vectors_ptr[2];
prim->type = 4;
prim->type = PRIM_GT4;
gte_nclip();
prim->drawMode = DRAW_HIDE;
gte_stopz(&nclip);

View File

@ -713,7 +713,7 @@ void RicEntityHitByIce(Entity* self) {
prim->drawMode = DRAW_UNK_200 | DRAW_UNK_100 | DRAW_TPAGE |
DRAW_COLORS | DRAW_TRANSP;
}
prim->type = 3;
prim->type = PRIM_G4;
prim->priority = PLAYER.zPriority + 2;
prim = prim->next;
}

View File

@ -38,13 +38,13 @@ void RicEntityTeleport(Entity* self) {
prim->y0 = 0;
prim->u0 = 0xC0;
prim->v0 = 0xF0;
prim->type = 1;
prim->type = PRIM_TILE;
prim->priority = 0x1FD;
prim->drawMode = DRAW_TPAGE2 | DRAW_TPAGE | DRAW_HIDE | DRAW_TRANSP;
prim = prim->next;
}
for (i = 0; i < 2; i++) {
prim->type = 3;
prim->type = PRIM_G4;
prim->priority = 0x1F8;
prim->drawMode = DRAW_TPAGE2 | DRAW_TPAGE | DRAW_TRANSP;
prim = prim->next;
@ -864,7 +864,7 @@ void func_80167A70(Entity* self) {
}
fakeprim->velocityY.val = -((rand() * 2) + FIX(2.5));
fakeprim->drawMode = DRAW_HIDE | DRAW_UNK02;
fakeprim->type = 1;
fakeprim->type = PRIM_TILE;
} else {
prim->r0 = prim->r1 = prim->r2 = prim->r3 =
(rand() & 0xF) | 0x30;
@ -883,7 +883,7 @@ void func_80167A70(Entity* self) {
prim->y1 = posY + D_80155D64[arrIndex][3];
prim->x3 = prim->x2 = posX + D_80155D64[arrIndex][4];
prim->y3 = prim->y2 = posY + D_80155D64[arrIndex][5];
prim->type = 3;
prim->type = PRIM_G4;
prim->priority = PLAYER.zPriority + 2;
}
}

View File

@ -2011,7 +2011,7 @@ void RicEntitySubwpnAgunea(Entity* self) {
PLAYER.posY.i.hi + PLAYER.hitboxOffY - 8;
self->posX.i.hi = self->ext.agunea.unk80 = PLAYER.posX.i.hi;
prim = &g_PrimBuf[self->primIndex];
prim->type = 2;
prim->type = PRIM_LINE_G2;
prim->priority = PLAYER.zPriority + 2;
prim->drawMode = DRAW_UNK_200 | DRAW_UNK_100 | DRAW_TPAGE2 |
DRAW_TPAGE | DRAW_TRANSP;

View File

@ -281,7 +281,7 @@ void RicEntityCrashCrossBeam(Entity* self) {
gte_ldv3(vectors_ptr[0], vectors_ptr[1], vectors_ptr[3]);
gte_rtpt();
temp_a3 = vectors_ptr[2];
prim->type = 4;
prim->type = PRIM_GT4;
gte_nclip();
prim->drawMode = DRAW_HIDE;
gte_stopz(&nclip);

View File

@ -149,7 +149,7 @@ void EntityStageNamePopup(Entity* self) {
prim = prim->next;
self->ext.stpopup.unk84 = prim;
prim->type = 3;
prim->type = PRIM_G4;
prim->tpage = 0x1A;
prim->clut = 0x15F;
prim->u0 = 0;

View File

@ -80,7 +80,7 @@ void EntityStageNamePopup(Entity* self) {
prim = prim->next;
prim = prim->next;
self->ext.stpopupj.unk84 = prim;
prim->type = 3;
prim->type = PRIM_G4;
prim->tpage = 0x1A;
prim->clut = 0x15F;
prim->u0 = 0;

View File

@ -83,7 +83,7 @@ void BottomCornerText(u8* str, u8 lowerLeft) {
#undef charcount
prim = &g_PrimBuf[g_unkGraphicsStruct.BottomCornerTextPrims];
prim->type = 3;
prim->type = PRIM_G4;
prim->b0 = prim->b1 = prim->b2 = prim->b3 = prim->g0 = prim->g1 = prim->g2 =
prim->g3 = prim->r0 = prim->r1 = prim->r2 = prim->r3 = 0;
if (lowerLeft) {
@ -132,7 +132,7 @@ void BottomCornerText(u8* str, u8 lowerLeft) {
prim->drawMode = DRAW_DEFAULT;
prim = prim->next;
prim->type = 4;
prim->type = PRIM_GT4;
prim->y0 = prim->y1 = 0xCD;
prim->tpage = 0x1F;
prim->clut = 0x197;

View File

@ -50,7 +50,7 @@ void BottomCornerText(u8* str, u8 lower_left) {
#undef charcount
prim = &g_PrimBuf[g_unkGraphicsStruct.BottomCornerTextPrims];
prim->type = 3;
prim->type = PRIM_G4;
prim->b0 = prim->b1 = prim->b2 = prim->b3 = prim->g0 = prim->g1 = prim->g2 =
prim->g3 = prim->r0 = prim->r1 = prim->r2 = prim->r3 = 0;
@ -100,7 +100,7 @@ void BottomCornerText(u8* str, u8 lower_left) {
prim->drawMode = DRAW_DEFAULT;
prim = prim->next;
prim->type = 4;
prim->type = PRIM_GT4;
prim->y0 = prim->y1 = 0xCD;
prim->tpage = 0x1F;
prim->clut = 0x197;

View File

@ -188,7 +188,7 @@ void PrologueScroll(void) {
}
for (i = 0; i < 4; i++) {
prim->type = 4;
prim->type = PRIM_GT4;
prim->u0 = prim->u2 = 0;
prim->u1 = prim->u3 = 0x80;

View File

@ -43,11 +43,11 @@ static u8 SetCutsceneScript(u8* script) {
prim->drawMode = DRAW_HIDE;
prim = g_Dialogue.prim[5] = prim->next;
prim->type = 4;
prim->type = PRIM_GT4;
prim->drawMode = DRAW_HIDE;
prim = prim->next;
prim->type = 3;
prim->type = PRIM_G4;
prim->r0 = prim->r1 = prim->r2 = prim->r3 = 0xFF;
prim->g0 = prim->g1 = prim->g2 = prim->g3 = 0;
prim->b0 = prim->b1 = prim->b2 = prim->b3 = 0;
@ -57,7 +57,7 @@ static u8 SetCutsceneScript(u8* script) {
prim->drawMode = DRAW_HIDE;
prim = prim->next;
prim->type = 1;
prim->type = PRIM_TILE;
prim->x0 = 3;
prim->y0 = 0x2F;
prim->v0 = 0x4A;

View File

@ -203,7 +203,7 @@ static void EntityWeaponAttack(Entity* self) {
prim->v2 = prim->v3 = var_s4 + 0x67;
prim->u0 = prim->u2 = 0x80;
prim->u1 = prim->u3 = 0x80 + 0x57;
prim->type = 4;
prim->type = PRIM_GT4;
prim->priority = 0x1B4;
prim->drawMode = DRAW_HIDE;
@ -239,7 +239,7 @@ static void EntityWeaponAttack(Entity* self) {
prim->r0 = prim->g0 = prim->b0 = prim->r1 = prim->g1 =
prim->b1 = prim->r2 = prim->g2 = prim->b2 = prim->r3 =
prim->g3 = prim->b3 = 0x80;
prim->type = 4;
prim->type = PRIM_GT4;
prim->priority = 0x1B6;
prim->drawMode = DRAW_TPAGE2 | DRAW_TPAGE | DRAW_HIDE |
DRAW_COLORS | DRAW_TRANSP;

View File

@ -77,7 +77,7 @@ void func_97000_8017AB54(u8* str, u8 lowerLeft) {
#undef charcount
prim = &g_PrimBuf[g_unkGraphicsStruct.BottomCornerTextPrims];
prim->type = 3;
prim->type = PRIM_G4;
prim->b0 = prim->b1 = prim->b2 = prim->b3 = prim->g0 = prim->g1 = prim->g2 =
prim->g3 = prim->r0 = prim->r1 = prim->r2 = prim->r3 = 0;
if (lowerLeft) {
@ -126,7 +126,7 @@ void func_97000_8017AB54(u8* str, u8 lowerLeft) {
prim->drawMode = DRAW_DEFAULT;
prim = prim->next;
prim->type = 4;
prim->type = PRIM_GT4;
prim->y0 = prim->y1 = 0xCD;
prim->tpage = 0x1F;
prim->clut = 0x197;

View File

@ -9,6 +9,7 @@ use num::Unsigned;
use regex::Regex;
use crate::line_transformer::LineTransformer;
use crate::enum_statement::EnumStatementMatcher;
pub trait EnumValue: Unsigned + 'static + Sync + Eq + Ord + PrimInt + FromStr { }
@ -69,9 +70,8 @@ impl<U: EnumValue> BitFlagLineTransformer<U> where <U as FromStr>::Err: Debug {
let mut _enum_values: Vec<&'static (U, &'static str)> = Vec::new();
_enum_values.extend_from_slice(&enum_values[..]);
_enum_values.sort_by(|(a, _), (b, _)| b.cmp(a));
let pattern = format!(r"([.>]{}\s*((?:(?:[&|=!^~]?)=)|(?:[&|^]))\s*)([~]?)((?:0x[A-Fa-f0-9]+)|0|(?:[1-9][0-9]*))([;),? ])", field_name.to_string());
let regex = Regex::new(&pattern).unwrap();
let regex = EnumStatementMatcher::new(None, field_name).regex().clone();
let safe_mask: U = enum_values.iter().fold(U::zero(), |mask, (bit, _)| mask | *bit);
Self {
@ -108,6 +108,7 @@ impl<U: EnumValue> BitFlagLineTransformer<U> where <U as FromStr>::Err: Debug {
field_value = !field_value;
}
// if more than half the bits are set, use the inverted value
let invert: String;
if field_value.count_ones() > ((size_of::<U>() as u32) * 8 / 2) {
invert = "~".to_string();

View File

@ -0,0 +1,87 @@
use std::cmp::Eq;
use std::cmp::Ord;
use std::fmt::Debug;
use std::str::FromStr;
use std::vec::Vec;
use num::PrimInt;
use num::Unsigned;
use regex::Regex;
use crate::line_transformer::LineTransformer;
use crate::enum_statement::EnumStatementMatcher;
pub trait EnumValue: Unsigned + 'static + Sync + Eq + Ord + PrimInt + FromStr { }
impl EnumValue for u8 where <u8 as FromStr>::Err: Debug { }
impl EnumValue for u16 where <u16 as FromStr>::Err: Debug { }
impl EnumValue for u32 where <u32 as FromStr>::Err: Debug { }
impl EnumValue for u64 where <u64 as FromStr>::Err: Debug { }
pub struct EnumLineTransformer<U: EnumValue> where <U as FromStr>::Err: Debug {
enum_values: Vec<&'static (U, &'static str)>,
regex: Regex,
}
impl<U: EnumValue> EnumLineTransformer<U> where <U as FromStr>::Err: Debug {
pub fn new(variable_name: Option<&str>, field_name: &str, enum_values: &Vec<&'static (U, &'static str)>) -> Self {
let mut _enum_values: Vec<&'static (U, &'static str)> = Vec::new();
_enum_values.extend_from_slice(&enum_values[..]);
_enum_values.sort_by(|(a, _), (b, _)| b.cmp(a));
let regex = EnumStatementMatcher::new(variable_name, field_name).regex().clone();
Self {
enum_values: _enum_values,
regex: regex,
}
}
fn replace_enum(&self, captures: &regex::Captures) -> String {
if let (Some(prefix), Some(field_value_string), Some(terminal_string)) = (
captures.get(1).map(|m| m.as_str().to_string()),
captures.get(4).map(|m| m.as_str().to_string()),
captures.get(5).map(|m| m.as_str().to_string())) {
let inverted = captures.get(3).map(|m| m.as_str()) == Some("~");
// if it starts with 0x, hex string, otherwise int
let mut field_value: U;
if field_value_string.starts_with("0x") {
if let Ok(v) = U::from_str_radix(field_value_string.strip_prefix("0x").unwrap(), 16) {
field_value = v;
} else {
return captures.get(0)
.map_or_else(|| "".to_string(), |m| m.as_str().to_string())
}
} else {
field_value = field_value_string.parse::<U>().unwrap();
}
if inverted {
field_value = !field_value;
}
if let Some(Some(rvalue)) = self.enum_values.iter()
.map(|(value, name)|
if *value == field_value {
Some(name)
} else {
None
}
)
.filter(|e| e.is_some())
.next() {
return format!("{}{}{}", prefix.to_string(), rvalue, terminal_string);
}
}
captures
.get(0)
.map_or_else(|| "".to_string(), |m| m.as_str().to_string())
}
}
impl<U: EnumValue> LineTransformer for EnumLineTransformer<U> where <U as FromStr>::Err: Debug {
fn transform_line(&self, line: &str) -> String where {
self.regex.replace_all(line, |captures: &regex::Captures| self.replace_enum(captures)).to_string()
}
}

View File

@ -0,0 +1,50 @@
use regex::Regex;
pub struct EnumStatementMatcher {
regex: Regex
}
impl EnumStatementMatcher {
pub fn new(variable_name: Option<&str>, field_name: &str) -> Self {
let var_and_operator_pattern = if let Some(var) = variable_name {
format!(r"(?x)
{} # the variable name
(?: # one of:
(?:->) | # * pointer field access operator
\. # * struct/union field access operator
)
", regex::escape(var))
} else {
// abbreviated pointer or struct field access
format!(r"[.>]")
};
let pattern = format!(r"(?x)
( # <1> a prefix containing:
{} # * a struct or pointer access operator
{} # * a field name (provided by format!)
\s* # * optional whitespace
( # * <2> a binary operator, one of:
(?:(?:[&|=!^~]?)=) | # * any comparison operator
(?:[&|^]) # * a bitwise operator
)
\s* # * optional whitespace
)
([~]?) # <3> an optional bitwise NOT
( # <4> the number, one of:
(?:0x[A-Fa-f0-9]+) | # * a hexidecimal integer
0 | # * a zero
(?:[1-9][0-9]*) # * a decimal integer
)
([;),? ]) # <5> a statement terminal
", var_and_operator_pattern, regex::escape(field_name));
Self { regex: Regex::new(&pattern).unwrap() }
}
pub fn regex(self: &Self) -> &Regex {
&self.regex
}
}

View File

@ -3,11 +3,14 @@ use std::io::{BufRead, BufReader, Write};
mod fixed;
mod line_transformer;
mod enum_statement;
mod enum_line_transformer;
mod bit_flag_line_transformer;
mod relics;
mod drawmodes;
mod flags;
mod drawflags;
mod primitive_type;
use line_transformer::LineTransformer;
use fixed::FixedTransformer;
@ -15,6 +18,7 @@ use relics::RelicsTransformer;
use drawmodes::DrawModeTransformer;
use flags::FlagsTransformer;
use drawflags::DrawFlagsTransformer;
use primitive_type::PrimitiveTypeTransformer;
use rayon::prelude::*;
fn transform_file(file_path: &str, transformers: &Vec<Box<dyn LineTransformer>>) -> usize {
@ -56,6 +60,7 @@ fn process_directory(dir_path: &str) {
let draw_mode_transformer = DrawModeTransformer::new();
let flags_transformer = FlagsTransformer::new();
let draw_flags_transformer = DrawFlagsTransformer::new();
let primitive_type_transformer = PrimitiveTypeTransformer::new();
let transformers: Vec<Box<dyn LineTransformer>> = vec![
Box::new(fixed_transformer),
@ -63,6 +68,7 @@ fn process_directory(dir_path: &str) {
Box::new(draw_mode_transformer),
Box::new(flags_transformer),
Box::new(draw_flags_transformer),
Box::new(primitive_type_transformer),
];
let entries = std::fs::read_dir(dir_path).expect("Unable to read directory");

View File

@ -0,0 +1,81 @@
use lazy_static::lazy_static;
use crate::line_transformer::LineTransformer;
use crate::enum_line_transformer::EnumLineTransformer;
pub struct PrimitiveTypeTransformer {
transformer: EnumLineTransformer<u16>,
}
lazy_static! {
static ref TYPES: [(u16, &'static str); 12] = [
(0, "PRIM_NONE"),
(1, "PRIM_TILE"),
(2, "PRIM_LINE_G2"),
(3, "PRIM_G4"),
(4, "PRIM_GT4"),
(5, "PRIM_GT3"),
(6, "PRIM_SPRT"),
(7, "PRIM_ENV"),
(8, "FLAG_DRAW_UNK100"),
(0x11, "PRIM_TITLE_ALT"),
(0x12, "PRIM_LINE_G2"),
(0x13, "PRIM_G4_ALT"),
];
}
impl PrimitiveTypeTransformer {
pub fn new() -> Self {
Self {
transformer: EnumLineTransformer::<u16>::new(
Some("prim"), "type", &TYPES.iter().collect()),
}
}
}
impl LineTransformer for PrimitiveTypeTransformer {
fn transform_line(&self, line: &str) -> String {
self.transformer.transform_line(line)
}
}
#[cfg(test)]
mod tests {
use super::*;
use once_cell::sync::Lazy;
static DMT: Lazy<PrimitiveTypeTransformer> = Lazy::new(|| PrimitiveTypeTransformer::new());
#[test]
fn test_type_hex() {
let input_line = "prim->type = 0x5;";
let expected_line = "prim->type = PRIM_GT3;";
let result = DMT.transform_line(input_line);
assert_eq!(result, expected_line)
}
#[test]
fn test_type_decimal() {
let input_line = "prim->type = 0x6, ";
let expected_line = "prim->type = PRIM_SPRT, ";
let result = DMT.transform_line(input_line);
assert_eq!(result, expected_line)
}
#[test]
fn test_type_zero_hex() {
let input_line = "(prim->type == 0x0)";
let expected_line = "(prim->type == PRIM_NONE)";
let result = DMT.transform_line(input_line);
assert_eq!(result, expected_line)
}
#[test]
fn test_type_not_prim() {
let input_line = "quad->type == 5;";
let expected_line = "quad->type == 5;";
let result = DMT.transform_line(input_line);
assert_eq!(result, expected_line)
}
}