use super ::{
ast::Profile,
error::ExpectedToken,
error::{Error, ErrorKind, ParseErrors},
token::TokenValue,
Frontend, Options, Span,
};
use crate ::ShaderStage;
use pp_rs::token::PreprocessorError;
#[ test]
fn version() {
let mut frontend = Frontend::default();
// invalid versions
assert_eq!(
frontend
.parse(
&Options::from(ShaderStage::Vertex),
"#version 99000\n void main(){}" ,
)
.err()
.unwrap(),
ParseErrors {
errors: vec![Error {
kind: ErrorKind::InvalidVersion(99000 ),
meta: Span::new(9 , 14 )
}],
},
);
assert_eq!(
frontend
.parse(
&Options::from(ShaderStage::Vertex),
"#version 449\n void main(){}" ,
)
.err()
.unwrap(),
ParseErrors {
errors: vec![Error {
kind: ErrorKind::InvalidVersion(449 ),
meta: Span::new(9 , 12 )
}]
},
);
assert_eq!(
frontend
.parse(
&Options::from(ShaderStage::Vertex),
"#version 450 smart\n void main(){}" ,
)
.err()
.unwrap(),
ParseErrors {
errors: vec![Error {
kind: ErrorKind::InvalidProfile("smart" .into()),
meta: Span::new(13 , 18 ),
}]
},
);
assert_eq!(
frontend
.parse(
&Options::from(ShaderStage::Vertex),
"#version 450\nvoid main(){} #version 450" ,
)
.err()
.unwrap(),
ParseErrors {
errors: vec![
Error {
kind: ErrorKind::PreprocessorError(PreprocessorError::UnexpectedHash,),
meta: Span::new(27 , 28 ),
},
Error {
kind: ErrorKind::InvalidToken(
TokenValue::Identifier("version" .into()),
vec![ExpectedToken::Eof]
),
meta: Span::new(28 , 35 )
}
]
},
);
// valid versions
frontend
.parse(
&Options::from(ShaderStage::Vertex),
" # version 450\nvoid main() {}" ,
)
.unwrap();
assert_eq!(
(frontend.metadata().version, frontend.metadata().profile),
(450 , Profile::Core)
);
frontend
.parse(
&Options::from(ShaderStage::Vertex),
"#version 450\nvoid main() {}" ,
)
.unwrap();
assert_eq!(
(frontend.metadata().version, frontend.metadata().profile),
(450 , Profile::Core)
);
frontend
.parse(
&Options::from(ShaderStage::Vertex),
"#version 450 core\nvoid main(void) {}" ,
)
.unwrap();
assert_eq!(
(frontend.metadata().version, frontend.metadata().profile),
(450 , Profile::Core)
);
}
#[ test]
fn control_flow() {
let mut frontend = Frontend::default();
frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
# version 450
void main() {
if (true ) {
return 1 ;
} else {
return 2 ;
}
}
"#,
)
.unwrap();
frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
# version 450
void main() {
if (true ) {
return 1 ;
}
}
"#,
)
.unwrap();
frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
# version 450
void main() {
int x;
int y = 3 ;
switch (5 ) {
case 2 :
x = 2 ;
case 5 :
x = 5 ;
y = 2 ;
break ;
default:
x = 0 ;
}
}
"#,
)
.unwrap();
frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
# version 450
void main() {
int x = 0 ;
while (x < 5 ) {
x = x + 1 ;
}
do {
x = x - 1 ;
} while (x >= 4 )
}
"#,
)
.unwrap();
frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
# version 450
void main() {
int x = 0 ;
for (int i = 0 ; i < 10 ;) {
x = x + 2 ;
}
for (;;);
return x;
}
"#,
)
.unwrap();
}
#[ test]
fn declarations() {
let mut frontend = Frontend::default();
frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
#version 450
layout(location = 0 ) in vec2 v_uv;
layout(location = 0 ) out vec4 o_color;
layout(set = 1 , binding = 1 ) uniform texture2D tex;
layout(set = 1 , binding = 2 ) uniform sampler tex_sampler;
layout(early_fragment_tests) in ;
void main() {}
"#,
)
.unwrap();
frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
#version 450
layout(std140, set = 2 , binding = 0 )
uniform u_locals {
vec3 model_offs;
float load_time;
ivec4 atlas_offs;
};
void main() {}
"#,
)
.unwrap();
frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
#version 450
layout(push_constant)
uniform u_locals {
vec3 model_offs;
float load_time;
ivec4 atlas_offs;
};
void main() {}
"#,
)
.unwrap();
frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
#version 450
layout(std430, set = 2 , binding = 0 )
uniform u_locals {
vec3 model_offs;
float load_time;
ivec4 atlas_offs;
};
void main() {}
"#,
)
.unwrap();
frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
#version 450
layout(std140, set = 2 , binding = 0 )
uniform u_locals {
vec3 model_offs;
float load_time;
} block_var;
void main() {
load_time * model_offs;
block_var.load_time * block_var.model_offs;
}
"#,
)
.unwrap();
frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
#version 450
float vector = vec4(1 .0 / 17 .0 , 9 .0 / 17 .0 , 3 .0 / 17 .0 , 11 .0 / 17 .0 );
void main() {}
"#,
)
.unwrap();
frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
#version 450
precision highp float;
void main() {}
"#,
)
.unwrap();
}
#[ test]
fn textures() {
let mut frontend = Frontend::default();
frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
#version 450
layout(location = 0 ) in vec2 v_uv;
layout(location = 0 ) out vec4 o_color;
layout(set = 1 , binding = 1 ) uniform texture2D tex;
layout(set = 1 , binding = 2 ) uniform sampler tex_sampler;
void main() {
o_color = texture(sampler2D(tex, tex_sampler), v_uv);
o_color.a = texture(sampler2D(tex, tex_sampler), v_uv, 2 .0 ).a;
}
"#,
)
.unwrap();
}
#[ test]
fn functions() {
let mut frontend = Frontend::default();
frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
# version 450
void test1(float);
void test1(float) {}
void main() {}
"#,
)
.unwrap();
frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
# version 450
void test2(float a) {}
void test3(float a, float b) {}
void test4(float, float) {}
void main() {}
"#,
)
.unwrap();
frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
# version 450
float test(float a) { return a; }
void main() {}
"#,
)
.unwrap();
frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
# version 450
float test(vec4 p) {
return p.x;
}
void main() {}
"#,
)
.unwrap();
// Function overloading
frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
# version 450
float test(vec2 p);
float test(vec3 p);
float test(vec4 p);
float test(vec2 p) {
return p.x;
}
float test(vec3 p) {
return p.x;
}
float test(vec4 p) {
return p.x;
}
void main() {}
"#,
)
.unwrap();
assert_eq!(
frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
# version 450
int test(vec4 p) {
return p.x;
}
float test(vec4 p) {
return p.x;
}
void main() {}
"#,
)
.err()
.unwrap(),
ParseErrors {
errors: vec![Error {
kind: ErrorKind::SemanticError("Function already defined" .into()),
meta: Span::new(134 , 152 ),
}]
},
);
println!();
frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
# version 450
float callee(uint q) {
return float(q);
}
float caller() {
callee(1 u);
}
void main() {}
"#,
)
.unwrap();
// Nested function call
frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
# version 450
layout(set = 0 , binding = 1 ) uniform texture2D t_noise;
layout(set = 0 , binding = 2 ) uniform sampler s_noise;
void main() {
textureLod(sampler2D(t_noise, s_noise), vec2(1 .0 ), 0 );
}
"#,
)
.unwrap();
frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
# version 450
void fun(vec2 in_parameter, out float out_parameter) {
ivec2 _ = ivec2(in_parameter);
}
void main() {
float a;
fun(vec2(1 .0 ), a);
}
"#,
)
.unwrap();
}
#[ test]
fn constants() {
use crate ::{Constant, Expression, Type , TypeInner};
let mut frontend = Frontend::default();
let module = frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
# version 450
const float a = 1 .0 ;
float global = a;
const float b = a;
void main() {}
"#,
)
.unwrap();
let mut types = module.types.iter();
let mut constants = module.constants.iter();
let mut global_expressions = module.global_expressions.iter();
let (ty_handle, ty) = types.next().unwrap();
assert_eq!(
ty,
&Type {
name: None,
inner: TypeInner::Scalar(crate ::Scalar::F32)
}
);
let (init_handle, init) = global_expressions.next().unwrap();
assert_eq!(init, &Expression::Literal(crate ::Literal::F32(1 .0 )));
assert_eq!(
constants.next().unwrap().1 ,
&Constant {
name: Some("a" .to_owned()),
ty: ty_handle,
init: init_handle
}
);
assert_eq!(
constants.next().unwrap().1 ,
&Constant {
name: Some("b" .to_owned()),
ty: ty_handle,
init: init_handle
}
);
assert!(constants.next().is_none());
}
#[ test]
fn function_overloading() {
let mut frontend = Frontend::default();
frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
# version 450
float saturate(float v) { return clamp(v, 0 .0 , 1 .0 ); }
vec2 saturate(vec2 v) { return clamp(v, vec2(0 .0 ), vec2(1 .0 )); }
vec3 saturate(vec3 v) { return clamp(v, vec3(0 .0 ), vec3(1 .0 )); }
vec4 saturate(vec4 v) { return clamp(v, vec4(0 .0 ), vec4(1 .0 )); }
void main() {
float v1 = saturate(1 .5 );
vec2 v2 = saturate(vec2(0 .5 , 1 .5 ));
vec3 v3 = saturate(vec3(0 .5 , 1 .5 , 2 .5 ));
vec3 v4 = saturate(vec4(0 .5 , 1 .5 , 2 .5 , 3 .5 ));
}
"#,
)
.unwrap();
}
#[ test]
fn implicit_conversions() {
let mut frontend = Frontend::default();
frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
# version 450
void main() {
mat4 a = mat4(1 );
float b = 1 u;
float c = 1 + 2 .0 ;
}
"#,
)
.unwrap();
assert_eq!(
frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
# version 450
void test(int a) {}
void test(uint a) {}
void main() {
test(1 .0 );
}
"#,
)
.err()
.unwrap(),
ParseErrors {
errors: vec![Error {
kind: ErrorKind::SemanticError("Unknown function \'test\'" .into()),
meta: Span::new(156 , 165 ),
}]
},
);
assert_eq!(
frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
# version 450
void test(float a) {}
void test(uint a) {}
void main() {
test(1 );
}
"#,
)
.err()
.unwrap(),
ParseErrors {
errors: vec![Error {
kind: ErrorKind::SemanticError("Ambiguous best function for \'test\'" .into()),
meta: Span::new(158 , 165 ),
}]
}
);
}
#[ test]
fn structs() {
let mut frontend = Frontend::default();
frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
# version 450
Test {
vec4 pos;
} xx;
void main() {}
"#,
)
.unwrap_err();
frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
# version 450
struct Test {
vec4 pos;
};
void main() {}
"#,
)
.unwrap();
frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
# version 450
const int NUM_VECS = 42 ;
struct Test {
vec4 vecs[NUM_VECS];
};
void main() {}
"#,
)
.unwrap();
frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
# version 450
struct Hello {
vec4 test;
} test() {
return Hello( vec4(1 .0 ) );
}
void main() {}
"#,
)
.unwrap();
frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
# version 450
struct Test {};
void main() {}
"#,
)
.unwrap_err();
frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
# version 450
inout struct Test {
vec4 x;
};
void main() {}
"#,
)
.unwrap_err();
}
#[ test]
fn swizzles() {
let mut frontend = Frontend::default();
frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
# version 450
void main() {
vec4 v = vec4(1 );
v.xyz = vec3(2 );
v.x = 5 .0 ;
v.xyz.zxy.yx.xy = vec2(5 .0 , 1 .0 );
}
"#,
)
.unwrap();
frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
# version 450
void main() {
vec4 v = vec4(1 );
v.xx = vec2(5 .0 );
}
"#,
)
.unwrap_err();
frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
# version 450
void main() {
vec3 v = vec3(1 );
v.w = 2 .0 ;
}
"#,
)
.unwrap_err();
}
#[ test]
fn expressions() {
let mut frontend = Frontend::default();
// Vector indexing
frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
# version 450
float test(int index) {
vec4 v = vec4(1 .0 , 2 .0 , 3 .0 , 4 .0 );
return v[index] + 1 .0 ;
}
void main() {}
"#,
)
.unwrap();
// Prefix increment/decrement
frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
# version 450
void main() {
uint index = 0 ;
--index;
++index;
}
"#,
)
.unwrap();
// Dynamic indexing of array
frontend
.parse(
&Options::from(ShaderStage::Vertex),
r#"
# version 450
void main() {
const vec4 positions[1 ] = { vec4(0 ) };
gl_Position = positions[gl_VertexIndex];
}
"#,
)
.unwrap();
}
Messung V0.5 in Prozent C=100 H=94 G=96
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-06-26)
¤
*© Formatika GbR, Deutschland