Show More
Commit Description:
Various UI improvements.
Commit Description:
Various UI improvements.
References:
File last commit:
Show/Diff file:
Action:
FNA/lib/MojoShader/utils/mojoshader-compiler.c
1102 lines | 35.0 KiB | text/x-c | CLexer
1102 lines | 35.0 KiB | text/x-c | CLexer
r0 | /** | |||
* MojoShader; generate shader programs from bytecode of compiled | ||||
* Direct3D shaders. | ||||
* | ||||
* Please see the file LICENSE.txt in the source's root directory. | ||||
* | ||||
* This file written by Ryan C. Gordon. | ||||
*/ | ||||
#include <stdio.h> | ||||
#include <stdlib.h> | ||||
#include <string.h> | ||||
#include <assert.h> | ||||
#include "mojoshader.h" | ||||
#ifdef _WIN32 | ||||
#define snprintf _snprintf // !!! FIXME: not a safe replacement! | ||||
#endif | ||||
static const char **include_paths = NULL; | ||||
static unsigned int include_path_count = 0; | ||||
#define MOJOSHADER_DEBUG_MALLOC 0 | ||||
#if MOJOSHADER_DEBUG_MALLOC | ||||
static void *Malloc(int len, void *d) | ||||
{ | ||||
void *ptr = malloc(len + sizeof (int)); | ||||
int *store = (int *) ptr; | ||||
printf("malloc() %d bytes (%p)\n", len, ptr); | ||||
if (ptr == NULL) return NULL; | ||||
*store = len; | ||||
return (void *) (store + 1); | ||||
} // Malloc | ||||
static void Free(void *_ptr, void *d) | ||||
{ | ||||
int *ptr = (((int *) _ptr) - 1); | ||||
int len = *ptr; | ||||
printf("free() %d bytes (%p)\n", len, ptr); | ||||
free(ptr); | ||||
} // Free | ||||
#else | ||||
#define Malloc NULL | ||||
#define Free NULL | ||||
#endif | ||||
static void fail(const char *err) | ||||
{ | ||||
printf("%s.\n", err); | ||||
exit(1); | ||||
} // fail | ||||
static void print_unroll_attr(FILE *io, const int unroll) | ||||
{ | ||||
// -1 means "unroll at compiler's discretion", | ||||
// -2 means user didn't specify the attribute. | ||||
switch (unroll) | ||||
{ | ||||
case 0: | ||||
fprintf(io, "[loop] "); | ||||
break; | ||||
case -1: | ||||
fprintf(io, "[unroll] "); | ||||
break; | ||||
case -2: | ||||
/* no-op. */ | ||||
break; | ||||
default: | ||||
assert(unroll > 0); | ||||
fprintf(io, "[unroll(%d)] ", unroll); | ||||
break; | ||||
} // case | ||||
} // print_unroll_attr | ||||
static void print_ast_datatype(FILE *io, const MOJOSHADER_astDataType *dt) | ||||
{ | ||||
int i; | ||||
if (dt == NULL) | ||||
return; | ||||
switch (dt->type) | ||||
{ | ||||
case MOJOSHADER_AST_DATATYPE_BOOL: | ||||
fprintf(io, "bool"); | ||||
return; | ||||
case MOJOSHADER_AST_DATATYPE_INT: | ||||
fprintf(io, "int"); | ||||
return; | ||||
case MOJOSHADER_AST_DATATYPE_UINT: | ||||
fprintf(io, "uint"); | ||||
return; | ||||
case MOJOSHADER_AST_DATATYPE_FLOAT: | ||||
fprintf(io, "float"); | ||||
return; | ||||
case MOJOSHADER_AST_DATATYPE_FLOAT_SNORM: | ||||
fprintf(io, "snorm float"); | ||||
return; | ||||
case MOJOSHADER_AST_DATATYPE_FLOAT_UNORM: | ||||
fprintf(io, "unorm float"); | ||||
return; | ||||
case MOJOSHADER_AST_DATATYPE_HALF: | ||||
fprintf(io, "half"); | ||||
return; | ||||
case MOJOSHADER_AST_DATATYPE_DOUBLE: | ||||
fprintf(io, "double"); | ||||
return; | ||||
case MOJOSHADER_AST_DATATYPE_STRING: | ||||
fprintf(io, "string"); | ||||
return; | ||||
case MOJOSHADER_AST_DATATYPE_SAMPLER_1D: | ||||
fprintf(io, "sampler1D"); | ||||
return; | ||||
case MOJOSHADER_AST_DATATYPE_SAMPLER_2D: | ||||
fprintf(io, "sampler2D"); | ||||
return; | ||||
case MOJOSHADER_AST_DATATYPE_SAMPLER_3D: | ||||
fprintf(io, "sampler3D"); | ||||
return; | ||||
case MOJOSHADER_AST_DATATYPE_SAMPLER_CUBE: | ||||
fprintf(io, "samplerCUBE"); | ||||
return; | ||||
case MOJOSHADER_AST_DATATYPE_SAMPLER_STATE: | ||||
fprintf(io, "sampler_state"); | ||||
return; | ||||
case MOJOSHADER_AST_DATATYPE_SAMPLER_COMPARISON_STATE: | ||||
fprintf(io, "SamplerComparisonState"); | ||||
return; | ||||
case MOJOSHADER_AST_DATATYPE_STRUCT: | ||||
fprintf(io, "struct { "); | ||||
for (i = 0; i < dt->structure.member_count; i++) | ||||
{ | ||||
print_ast_datatype(io, dt->structure.members[i].datatype); | ||||
fprintf(io, " %s; ", dt->structure.members[i].identifier); | ||||
} // for | ||||
fprintf(io, "}"); | ||||
return; | ||||
case MOJOSHADER_AST_DATATYPE_ARRAY: | ||||
print_ast_datatype(io, dt->array.base); | ||||
if (dt->array.elements < 0) | ||||
fprintf(io, "[]"); | ||||
else | ||||
fprintf(io, "[%d]", dt->array.elements); | ||||
return; | ||||
case MOJOSHADER_AST_DATATYPE_VECTOR: | ||||
fprintf(io, "vector<"); | ||||
print_ast_datatype(io, dt->vector.base); | ||||
fprintf(io, ",%d>", dt->vector.elements); | ||||
return; | ||||
case MOJOSHADER_AST_DATATYPE_MATRIX: | ||||
fprintf(io, "matrix<"); | ||||
print_ast_datatype(io, dt->matrix.base); | ||||
fprintf(io, ",%d,%d>", dt->matrix.rows, dt->matrix.columns); | ||||
return; | ||||
case MOJOSHADER_AST_DATATYPE_BUFFER: | ||||
fprintf(io, "buffer<"); | ||||
print_ast_datatype(io, dt->buffer.base); | ||||
fprintf(io, ">"); | ||||
return; | ||||
case MOJOSHADER_AST_DATATYPE_USER: | ||||
fprintf(io, "%s", dt->user.name); | ||||
return; | ||||
// this should only appear if we did semantic analysis on the AST, | ||||
// so we only print the return value here. | ||||
case MOJOSHADER_AST_DATATYPE_FUNCTION: | ||||
if (!dt->function.retval) | ||||
fprintf(io, "void"); | ||||
else | ||||
print_ast_datatype(io, dt->function.retval); | ||||
return; | ||||
//case MOJOSHADER_AST_DATATYPE_NONE: | ||||
default: | ||||
assert(0 && "Unexpected datatype."); | ||||
return; | ||||
} // switch | ||||
} // print_ast_datatype | ||||
// !!! FIXME: this screws up on order of operations. | ||||
static void print_ast(FILE *io, const int substmt, const void *_ast) | ||||
{ | ||||
const MOJOSHADER_astNode *ast = (const MOJOSHADER_astNode *) _ast; | ||||
const char *nl = substmt ? "" : "\n"; | ||||
int typeint = 0; | ||||
static int indent = 0; | ||||
int isblock = 0; | ||||
int i; | ||||
// These _HAVE_ to be in the same order as MOJOSHADER_astNodeType! | ||||
static const char *binary[] = | ||||
{ | ||||
",", "*", "/", "%", "+", "-", "<<", ">>", "<", ">", "<=", ">=", "==", | ||||
"!=", "&", "^", "|", "&&", "||", "=", "*=", "/=", "%=", "+=", "-=", | ||||
"<<=", ">>=", "&=", "^=", "|=" | ||||
}; | ||||
static const char *pre_unary[] = { "++", "--", "-", "~", "!" }; | ||||
static const char *post_unary[] = { "++", "--" }; | ||||
static const char *simple_stmt[] = { "", "break", "continue", "discard" }; | ||||
static const char *inpmod[] = { "", "in ", "out ", "in out ", "uniform " }; | ||||
static const char *fnstorage[] = { "", "inline " }; | ||||
static const char *interpmod[] = { | ||||
"", " linear", " centroid", " nointerpolation", | ||||
" noperspective", " sample" | ||||
}; | ||||
if (!ast) return; | ||||
typeint = (int) ast->ast.type; | ||||
#define DO_INDENT do { \ | ||||
if (!substmt) { for (i = 0; i < indent; i++) fprintf(io, " "); } \ | ||||
} while (0) | ||||
switch (ast->ast.type) | ||||
{ | ||||
case MOJOSHADER_AST_OP_PREINCREMENT: | ||||
case MOJOSHADER_AST_OP_PREDECREMENT: | ||||
case MOJOSHADER_AST_OP_NEGATE: | ||||
case MOJOSHADER_AST_OP_COMPLEMENT: | ||||
case MOJOSHADER_AST_OP_NOT: | ||||
fprintf(io, "%s", pre_unary[(typeint-MOJOSHADER_AST_OP_START_RANGE_UNARY)-1]); | ||||
print_ast(io, 0, ast->unary.operand); | ||||
break; | ||||
case MOJOSHADER_AST_OP_POSTINCREMENT: | ||||
case MOJOSHADER_AST_OP_POSTDECREMENT: | ||||
print_ast(io, 0, ast->unary.operand); | ||||
fprintf(io, "%s", post_unary[typeint-MOJOSHADER_AST_OP_POSTINCREMENT]); | ||||
break; | ||||
case MOJOSHADER_AST_OP_MULTIPLY: | ||||
case MOJOSHADER_AST_OP_DIVIDE: | ||||
case MOJOSHADER_AST_OP_MODULO: | ||||
case MOJOSHADER_AST_OP_ADD: | ||||
case MOJOSHADER_AST_OP_SUBTRACT: | ||||
case MOJOSHADER_AST_OP_LSHIFT: | ||||
case MOJOSHADER_AST_OP_RSHIFT: | ||||
case MOJOSHADER_AST_OP_LESSTHAN: | ||||
case MOJOSHADER_AST_OP_GREATERTHAN: | ||||
case MOJOSHADER_AST_OP_LESSTHANOREQUAL: | ||||
case MOJOSHADER_AST_OP_GREATERTHANOREQUAL: | ||||
case MOJOSHADER_AST_OP_EQUAL: | ||||
case MOJOSHADER_AST_OP_NOTEQUAL: | ||||
case MOJOSHADER_AST_OP_BINARYAND: | ||||
case MOJOSHADER_AST_OP_BINARYXOR: | ||||
case MOJOSHADER_AST_OP_BINARYOR: | ||||
case MOJOSHADER_AST_OP_LOGICALAND: | ||||
case MOJOSHADER_AST_OP_LOGICALOR: | ||||
case MOJOSHADER_AST_OP_ASSIGN: | ||||
case MOJOSHADER_AST_OP_MULASSIGN: | ||||
case MOJOSHADER_AST_OP_DIVASSIGN: | ||||
case MOJOSHADER_AST_OP_MODASSIGN: | ||||
case MOJOSHADER_AST_OP_ADDASSIGN: | ||||
case MOJOSHADER_AST_OP_SUBASSIGN: | ||||
case MOJOSHADER_AST_OP_LSHIFTASSIGN: | ||||
case MOJOSHADER_AST_OP_RSHIFTASSIGN: | ||||
case MOJOSHADER_AST_OP_ANDASSIGN: | ||||
case MOJOSHADER_AST_OP_XORASSIGN: | ||||
case MOJOSHADER_AST_OP_ORASSIGN: | ||||
case MOJOSHADER_AST_OP_COMMA: | ||||
print_ast(io, 0, ast->binary.left); | ||||
if (ast->ast.type != MOJOSHADER_AST_OP_COMMA) | ||||
fprintf(io, " "); // no space before the comma. | ||||
fprintf(io, "%s ", binary[ | ||||
(typeint - MOJOSHADER_AST_OP_START_RANGE_BINARY) - 1]); | ||||
print_ast(io, 0, ast->binary.right); | ||||
break; | ||||
case MOJOSHADER_AST_OP_DEREF_ARRAY: | ||||
print_ast(io, 0, ast->binary.left); | ||||
fprintf(io, "["); | ||||
print_ast(io, 0, ast->binary.right); | ||||
fprintf(io, "]"); | ||||
break; | ||||
case MOJOSHADER_AST_OP_DEREF_STRUCT: | ||||
print_ast(io, 0, ast->derefstruct.identifier); | ||||
fprintf(io, "."); | ||||
fprintf(io, "%s", ast->derefstruct.member); | ||||
break; | ||||
case MOJOSHADER_AST_OP_CONDITIONAL: | ||||
print_ast(io, 0, ast->ternary.left); | ||||
fprintf(io, " ? "); | ||||
print_ast(io, 0, ast->ternary.center); | ||||
fprintf(io, " : "); | ||||
print_ast(io, 0, ast->ternary.right); | ||||
break; | ||||
case MOJOSHADER_AST_OP_IDENTIFIER: | ||||
fprintf(io, "%s", ast->identifier.identifier); | ||||
break; | ||||
case MOJOSHADER_AST_OP_INT_LITERAL: | ||||
fprintf(io, "%d", ast->intliteral.value); | ||||
break; | ||||
case MOJOSHADER_AST_OP_FLOAT_LITERAL: | ||||
{ | ||||
const float f = ast->floatliteral.value; | ||||
const long long flr = (long long) f; | ||||
if (((float) flr) == f) | ||||
fprintf(io, "%lld.0", flr); | ||||
else | ||||
fprintf(io, "%.16g", f); | ||||
break; | ||||
} // case | ||||
case MOJOSHADER_AST_OP_STRING_LITERAL: | ||||
fprintf(io, "\"%s\"", ast->stringliteral.string); | ||||
break; | ||||
case MOJOSHADER_AST_OP_BOOLEAN_LITERAL: | ||||
fprintf(io, "%s", ast->boolliteral.value ? "true" : "false"); | ||||
break; | ||||
case MOJOSHADER_AST_ARGUMENTS: | ||||
print_ast(io, 0, ast->arguments.argument); | ||||
if (ast->arguments.next != NULL) | ||||
{ | ||||
fprintf(io, ", "); | ||||
print_ast(io, 0, ast->arguments.next); | ||||
} // if | ||||
break; | ||||
case MOJOSHADER_AST_OP_CALLFUNC: | ||||
print_ast(io, 0, ast->callfunc.identifier); | ||||
fprintf(io, "("); | ||||
print_ast(io, 0, ast->callfunc.args); | ||||
fprintf(io, ")"); | ||||
break; | ||||
case MOJOSHADER_AST_OP_CONSTRUCTOR: | ||||
print_ast_datatype(io, ast->constructor.datatype); | ||||
fprintf(io, "("); | ||||
print_ast(io, 0, ast->constructor.args); | ||||
fprintf(io, ")"); | ||||
break; | ||||
case MOJOSHADER_AST_OP_CAST: | ||||
fprintf(io, "("); | ||||
print_ast_datatype(io, ast->cast.datatype); | ||||
fprintf(io, ") ("); | ||||
print_ast(io, 0, ast->cast.operand); | ||||
fprintf(io, ")"); | ||||
break; | ||||
case MOJOSHADER_AST_STATEMENT_EXPRESSION: | ||||
DO_INDENT; | ||||
print_ast(io, 0, ast->exprstmt.expr); // !!! FIXME: This is named badly... | ||||
fprintf(io, ";%s", nl); | ||||
print_ast(io, 0, ast->exprstmt.next); | ||||
break; | ||||
case MOJOSHADER_AST_STATEMENT_IF: | ||||
DO_INDENT; | ||||
fprintf(io, "if ("); | ||||
print_ast(io, 0, ast->ifstmt.expr); | ||||
fprintf(io, ")\n"); | ||||
isblock = ast->ifstmt.statement->ast.type == MOJOSHADER_AST_STATEMENT_BLOCK; | ||||
if (!isblock) indent++; | ||||
print_ast(io, 0, ast->ifstmt.statement); | ||||
if (!isblock) indent--; | ||||
print_ast(io, 0, ast->ifstmt.next); | ||||
break; | ||||
case MOJOSHADER_AST_STATEMENT_TYPEDEF: | ||||
DO_INDENT; | ||||
print_ast(io, 1, ast->typedefstmt.type_info); | ||||
fprintf(io, "%s", nl); | ||||
print_ast(io, 0, ast->typedefstmt.next); | ||||
break; | ||||
case MOJOSHADER_AST_STATEMENT_SWITCH: | ||||
DO_INDENT; | ||||
switch ( ast->switchstmt.attributes ) | ||||
{ | ||||
case MOJOSHADER_AST_SWITCHATTR_NONE: break; | ||||
case MOJOSHADER_AST_SWITCHATTR_FLATTEN: fprintf(io, "[flatten] "); break; | ||||
case MOJOSHADER_AST_SWITCHATTR_BRANCH: fprintf(io, "[branch] "); break; | ||||
case MOJOSHADER_AST_SWITCHATTR_FORCECASE: fprintf(io, "[forcecase] "); break; | ||||
case MOJOSHADER_AST_SWITCHATTR_CALL: fprintf(io, "[call] "); break; | ||||
} // switch | ||||
fprintf(io, "switch ("); | ||||
print_ast(io, 0, ast->switchstmt.expr); | ||||
fprintf(io, ")\n"); | ||||
DO_INDENT; | ||||
fprintf(io, "{\n"); | ||||
indent++; | ||||
print_ast(io, 0, ast->switchstmt.cases); | ||||
indent--; | ||||
fprintf(io, "\n"); | ||||
DO_INDENT; | ||||
fprintf(io, "}\n"); | ||||
print_ast(io, 0, ast->switchstmt.next); | ||||
break; | ||||
case MOJOSHADER_AST_SWITCH_CASE: | ||||
DO_INDENT; | ||||
fprintf(io, "case "); | ||||
print_ast(io, 0, ast->cases.expr); | ||||
fprintf(io, ":\n"); | ||||
isblock = ast->cases.statement->ast.type == MOJOSHADER_AST_STATEMENT_BLOCK; | ||||
if (!isblock) indent++; | ||||
print_ast(io, 0, ast->cases.statement); | ||||
if (!isblock) indent--; | ||||
print_ast(io, 0, ast->cases.next); | ||||
break; | ||||
case MOJOSHADER_AST_STATEMENT_STRUCT: | ||||
DO_INDENT; | ||||
print_ast(io, 0, ast->structstmt.struct_info); | ||||
fprintf(io, ";%s%s", nl, nl); // always space these out. | ||||
print_ast(io, 0, ast->structstmt.next); | ||||
break; | ||||
case MOJOSHADER_AST_STATEMENT_VARDECL: | ||||
DO_INDENT; | ||||
print_ast(io, 1, ast->vardeclstmt.declaration); | ||||
fprintf(io, ";%s", nl); | ||||
print_ast(io, 0, ast->vardeclstmt.next); | ||||
break; | ||||
case MOJOSHADER_AST_STATEMENT_BLOCK: | ||||
DO_INDENT; | ||||
fprintf(io, "{\n"); | ||||
indent++; | ||||
print_ast(io, 0, ast->blockstmt.statements); | ||||
indent--; | ||||
DO_INDENT; | ||||
fprintf(io, "}\n"); | ||||
print_ast(io, 0, ast->blockstmt.next); | ||||
break; | ||||
case MOJOSHADER_AST_STATEMENT_FOR: | ||||
DO_INDENT; | ||||
print_unroll_attr(io, ast->forstmt.unroll); | ||||
fprintf(io, "for ("); | ||||
print_ast(io, 1, ast->forstmt.var_decl); | ||||
print_ast(io, 1, ast->forstmt.initializer); | ||||
fprintf(io, "; "); | ||||
print_ast(io, 1, ast->forstmt.looptest); | ||||
fprintf(io, "; "); | ||||
print_ast(io, 1, ast->forstmt.counter); | ||||
fprintf(io, ")\n"); | ||||
isblock = ast->forstmt.statement->ast.type == MOJOSHADER_AST_STATEMENT_BLOCK; | ||||
if (!isblock) indent++; | ||||
print_ast(io, 0, ast->forstmt.statement); | ||||
if (!isblock) indent--; | ||||
print_ast(io, 0, ast->forstmt.next); | ||||
break; | ||||
case MOJOSHADER_AST_STATEMENT_DO: | ||||
DO_INDENT; | ||||
print_unroll_attr(io, ast->dostmt.unroll); | ||||
fprintf(io, "do\n"); | ||||
isblock = ast->dostmt.statement->ast.type == MOJOSHADER_AST_STATEMENT_BLOCK; | ||||
if (!isblock) indent++; | ||||
print_ast(io, 0, ast->dostmt.statement); | ||||
if (!isblock) indent--; | ||||
DO_INDENT; | ||||
fprintf(io, "while ("); | ||||
print_ast(io, 0, ast->dostmt.expr); | ||||
fprintf(io, ");\n"); | ||||
print_ast(io, 0, ast->dostmt.next); | ||||
break; | ||||
case MOJOSHADER_AST_STATEMENT_WHILE: | ||||
DO_INDENT; | ||||
print_unroll_attr(io, ast->whilestmt.unroll); | ||||
fprintf(io, "while ("); | ||||
print_ast(io, 0, ast->whilestmt.expr); | ||||
fprintf(io, ")\n"); | ||||
isblock = ast->whilestmt.statement->ast.type == MOJOSHADER_AST_STATEMENT_BLOCK; | ||||
if (!isblock) indent++; | ||||
print_ast(io, 0, ast->whilestmt.statement); | ||||
if (!isblock) indent--; | ||||
print_ast(io, 0, ast->whilestmt.next); | ||||
break; | ||||
case MOJOSHADER_AST_STATEMENT_RETURN: | ||||
DO_INDENT; | ||||
fprintf(io, "return"); | ||||
if (ast->returnstmt.expr) | ||||
{ | ||||
fprintf(io, " "); | ||||
print_ast(io, 0, ast->returnstmt.expr); | ||||
} // if | ||||
fprintf(io, ";%s", nl); | ||||
print_ast(io, 0, ast->returnstmt.next); | ||||
break; | ||||
case MOJOSHADER_AST_STATEMENT_EMPTY: | ||||
case MOJOSHADER_AST_STATEMENT_BREAK: | ||||
case MOJOSHADER_AST_STATEMENT_CONTINUE: | ||||
case MOJOSHADER_AST_STATEMENT_DISCARD: | ||||
DO_INDENT; | ||||
fprintf(io, "%s;%s", | ||||
simple_stmt[(typeint-MOJOSHADER_AST_STATEMENT_START_RANGE)-1], | ||||
nl); | ||||
print_ast(io, 0, ast->stmt.next); | ||||
break; | ||||
case MOJOSHADER_AST_COMPUNIT_FUNCTION: | ||||
DO_INDENT; | ||||
print_ast(io, 0, ast->funcunit.declaration); | ||||
if (ast->funcunit.definition == NULL) | ||||
fprintf(io, ";%s", nl); | ||||
else | ||||
{ | ||||
fprintf(io, "%s", nl); | ||||
print_ast(io, 0, ast->funcunit.definition); | ||||
fprintf(io, "%s", nl); | ||||
} // else | ||||
print_ast(io, 0, ast->funcunit.next); | ||||
break; | ||||
case MOJOSHADER_AST_COMPUNIT_TYPEDEF: | ||||
DO_INDENT; | ||||
print_ast(io, 0, ast->typedefunit.type_info); | ||||
fprintf(io, "%s", nl); | ||||
print_ast(io, 0, ast->typedefunit.next); | ||||
break; | ||||
case MOJOSHADER_AST_COMPUNIT_STRUCT: | ||||
DO_INDENT; | ||||
print_ast(io, 0, ast->structunit.struct_info); | ||||
fprintf(io, ";%s%s", nl, nl); // always space these out. | ||||
print_ast(io, 0, ast->structunit.next); | ||||
break; | ||||
case MOJOSHADER_AST_COMPUNIT_VARIABLE: | ||||
DO_INDENT; | ||||
print_ast(io, 1, ast->varunit.declaration); | ||||
fprintf(io, ";%s", nl); | ||||
if (ast->varunit.next && | ||||
ast->varunit.next->ast.type!=MOJOSHADER_AST_COMPUNIT_VARIABLE) | ||||
{ | ||||
fprintf(io, "%s", nl); // group vars together, and space out other things. | ||||
} // if | ||||
print_ast(io, 0, ast->varunit.next); | ||||
break; | ||||
case MOJOSHADER_AST_SCALAR_OR_ARRAY: | ||||
fprintf(io, "%s", ast->soa.identifier); | ||||
if (ast->soa.isarray) | ||||
{ | ||||
fprintf(io, "["); | ||||
print_ast(io, 0, ast->soa.dimension); | ||||
fprintf(io, "]"); | ||||
} // if | ||||
break; | ||||
case MOJOSHADER_AST_TYPEDEF: | ||||
DO_INDENT; | ||||
fprintf(io, "typedef %s", ast->typdef.isconst ? "const " : ""); | ||||
print_ast_datatype(io, ast->typdef.datatype); | ||||
fprintf(io, " "); | ||||
print_ast(io, 0, ast->typdef.details); | ||||
fprintf(io, ";%s", nl); | ||||
break; | ||||
case MOJOSHADER_AST_FUNCTION_PARAMS: | ||||
fprintf(io, "%s", inpmod[(int) ast->params.input_modifier]); | ||||
print_ast_datatype(io, ast->params.datatype); | ||||
fprintf(io, " %s", ast->params.identifier); | ||||
if (ast->params.semantic) | ||||
fprintf(io, " : %s", ast->params.semantic); | ||||
fprintf(io, "%s", interpmod[(int) ast->params.interpolation_modifier]); | ||||
if (ast->params.initializer) | ||||
{ | ||||
fprintf(io, " = "); | ||||
print_ast(io, 0, ast->params.initializer); | ||||
} // if | ||||
if (ast->params.next) | ||||
{ | ||||
fprintf(io, ", "); | ||||
print_ast(io, 0, ast->params.next); | ||||
} // if | ||||
break; | ||||
case MOJOSHADER_AST_FUNCTION_SIGNATURE: | ||||
fprintf(io, "%s", fnstorage[(int) ast->funcsig.storage_class]); | ||||
if (ast->funcsig.datatype) | ||||
print_ast_datatype(io, ast->funcsig.datatype); | ||||
else | ||||
fprintf(io, "void"); | ||||
fprintf(io, " %s(", ast->funcsig.identifier); | ||||
print_ast(io, 0, ast->funcsig.params); | ||||
fprintf(io, ")"); | ||||
if (ast->funcsig.semantic) | ||||
fprintf(io, " : %s", ast->funcsig.semantic); | ||||
break; | ||||
case MOJOSHADER_AST_STRUCT_DECLARATION: | ||||
fprintf(io, "struct %s\n", ast->structdecl.name); | ||||
DO_INDENT; | ||||
fprintf(io, "{\n"); | ||||
indent++; | ||||
print_ast(io, 0, ast->structdecl.members); | ||||
indent--; | ||||
DO_INDENT; | ||||
fprintf(io, "}"); | ||||
break; | ||||
case MOJOSHADER_AST_STRUCT_MEMBER: | ||||
DO_INDENT; | ||||
fprintf(io, "%s", interpmod[(int)ast->structmembers.interpolation_mod]); | ||||
print_ast_datatype(io, ast->structmembers.datatype); | ||||
fprintf(io, " "); | ||||
print_ast(io, 0, ast->structmembers.details); | ||||
if (ast->structmembers.semantic) | ||||
fprintf(io, " : %s", ast->structmembers.semantic); | ||||
fprintf(io, ";%s", nl); | ||||
print_ast(io, 0, ast->structmembers.next); | ||||
break; | ||||
case MOJOSHADER_AST_VARIABLE_DECLARATION: | ||||
DO_INDENT; | ||||
if (ast->vardecl.attributes & MOJOSHADER_AST_VARATTR_EXTERN) | ||||
fprintf(io, "extern "); | ||||
if (ast->vardecl.attributes & MOJOSHADER_AST_VARATTR_NOINTERPOLATION) | ||||
fprintf(io, "nointerpolation "); | ||||
if (ast->vardecl.attributes & MOJOSHADER_AST_VARATTR_SHARED) | ||||
fprintf(io, "shared"); | ||||
if (ast->vardecl.attributes & MOJOSHADER_AST_VARATTR_STATIC) | ||||
fprintf(io, "static "); | ||||
if (ast->vardecl.attributes & MOJOSHADER_AST_VARATTR_UNIFORM) | ||||
fprintf(io, "uniform "); | ||||
if (ast->vardecl.attributes & MOJOSHADER_AST_VARATTR_VOLATILE) | ||||
fprintf(io, "nointerpolation "); | ||||
if (ast->vardecl.attributes & MOJOSHADER_AST_VARATTR_CONST) | ||||
fprintf(io, "const "); | ||||
if (ast->vardecl.attributes & MOJOSHADER_AST_VARATTR_ROWMAJOR) | ||||
fprintf(io, "rowmajor "); | ||||
if (ast->vardecl.attributes & MOJOSHADER_AST_VARATTR_COLUMNMAJOR) | ||||
fprintf(io, "columnmajor "); | ||||
if (ast->vardecl.datatype) | ||||
print_ast_datatype(io, ast->vardecl.datatype); | ||||
else | ||||
print_ast(io, 0, ast->vardecl.anonymous_datatype); | ||||
fprintf(io, " "); | ||||
print_ast(io, 0, ast->vardecl.details); | ||||
if (ast->vardecl.semantic) | ||||
fprintf(io, " : %s", ast->vardecl.semantic); | ||||
if (ast->vardecl.annotations) | ||||
{ | ||||
fprintf(io, " "); | ||||
print_ast(io, 0, ast->vardecl.annotations); | ||||
} // if | ||||
if (ast->vardecl.initializer != NULL) | ||||
{ | ||||
fprintf(io, " = "); | ||||
print_ast(io, 0, ast->vardecl.initializer); | ||||
} // if | ||||
print_ast(io, 0, ast->vardecl.lowlevel); | ||||
if (ast->vardecl.next == NULL) | ||||
fprintf(io, "%s", nl); | ||||
else | ||||
{ | ||||
const int attr = ast->vardecl.next->attributes; | ||||
fprintf(io, ", "); | ||||
ast->vardecl.next->attributes = 0; | ||||
print_ast(io, 1, ast->vardecl.next); | ||||
ast->vardecl.next->attributes = attr; | ||||
} // if | ||||
break; | ||||
case MOJOSHADER_AST_PACK_OFFSET: | ||||
fprintf(io, " : packoffset(%s%s%s)", ast->packoffset.ident1, | ||||
ast->packoffset.ident2 ? "." : "", | ||||
ast->packoffset.ident2 ? ast->packoffset.ident2 : ""); | ||||
break; | ||||
case MOJOSHADER_AST_VARIABLE_LOWLEVEL: | ||||
print_ast(io, 0, ast->varlowlevel.packoffset); | ||||
if (ast->varlowlevel.register_name) | ||||
fprintf(io, " : register(%s)", ast->varlowlevel.register_name); | ||||
break; | ||||
case MOJOSHADER_AST_ANNOTATION: | ||||
{ | ||||
const MOJOSHADER_astAnnotations *a = &ast->annotations; | ||||
fprintf(io, "<"); | ||||
while (a) | ||||
{ | ||||
fprintf(io, " "); | ||||
print_ast_datatype(io, a->datatype); | ||||
if (a->initializer != NULL) | ||||
{ | ||||
fprintf(io, " = "); | ||||
print_ast(io, 0, a->initializer); | ||||
} // if | ||||
if (a->next) | ||||
fprintf(io, ","); | ||||
a = a->next; | ||||
} // while | ||||
fprintf(io, " >"); | ||||
break; | ||||
} // case | ||||
default: | ||||
assert(0 && "unexpected type"); | ||||
break; | ||||
} // switch | ||||
#undef DO_INDENT | ||||
} // print_ast | ||||
static int open_include(MOJOSHADER_includeType inctype, const char *fname, | ||||
const char *parent, const char **outdata, | ||||
unsigned int *outbytes, MOJOSHADER_malloc m, | ||||
MOJOSHADER_free f, void *d) | ||||
{ | ||||
int i; | ||||
for (i = 0; i < include_path_count; i++) | ||||
{ | ||||
const char *path = include_paths[i]; | ||||
const size_t len = strlen(path) + strlen(fname) + 2; | ||||
char *buf = (char *) m(len, d); | ||||
if (buf == NULL) | ||||
return 0; | ||||
snprintf(buf, len, "%s/%s", path, fname); | ||||
FILE *io = fopen(buf, "rb"); | ||||
f(buf, d); | ||||
if (io == NULL) | ||||
continue; | ||||
if (fseek(io, 0, SEEK_END) != -1) | ||||
{ | ||||
const long fsize = ftell(io); | ||||
if ((fsize == -1) || (fseek(io, 0, SEEK_SET) == -1)) | ||||
{ | ||||
fclose(io); | ||||
return 0; | ||||
} // if | ||||
char *data = (char *) m(fsize, d); | ||||
if (data == NULL) | ||||
{ | ||||
fclose(io); | ||||
return 0; | ||||
} // if | ||||
if (fread(data, fsize, 1, io) != 1) | ||||
{ | ||||
f(data, d); | ||||
fclose(io); | ||||
return 0; | ||||
} // if | ||||
fclose(io); | ||||
*outdata = data; | ||||
*outbytes = (unsigned int) fsize; | ||||
return 1; | ||||
} // if | ||||
} // for | ||||
return 0; | ||||
} // open_include | ||||
static void close_include(const char *data, MOJOSHADER_malloc m, | ||||
MOJOSHADER_free f, void *d) | ||||
{ | ||||
f((void *) data, d); | ||||
} // close_include | ||||
static int preprocess(const char *fname, const char *buf, int len, | ||||
const char *outfile, | ||||
const MOJOSHADER_preprocessorDefine *defs, | ||||
unsigned int defcount, FILE *io) | ||||
{ | ||||
const MOJOSHADER_preprocessData *pd; | ||||
int retval = 0; | ||||
pd = MOJOSHADER_preprocess(fname, buf, len, defs, defcount, open_include, | ||||
close_include, Malloc, Free, NULL); | ||||
if (pd->error_count > 0) | ||||
{ | ||||
int i; | ||||
for (i = 0; i < pd->error_count; i++) | ||||
{ | ||||
fprintf(stderr, "%s:%d: ERROR: %s\n", | ||||
pd->errors[i].filename ? pd->errors[i].filename : "???", | ||||
pd->errors[i].error_position, | ||||
pd->errors[i].error); | ||||
} // for | ||||
} // if | ||||
else | ||||
{ | ||||
if (pd->output != NULL) | ||||
{ | ||||
const int len = pd->output_len; | ||||
if ((len) && (fwrite(pd->output, len, 1, io) != 1)) | ||||
printf(" ... fwrite('%s') failed.\n", outfile); | ||||
else if ((outfile != NULL) && (fclose(io) == EOF)) | ||||
printf(" ... fclose('%s') failed.\n", outfile); | ||||
else | ||||
retval = 1; | ||||
} // if | ||||
} // else | ||||
MOJOSHADER_freePreprocessData(pd); | ||||
return retval; | ||||
} // preprocess | ||||
static int assemble(const char *fname, const char *buf, int len, | ||||
const char *outfile, | ||||
const MOJOSHADER_preprocessorDefine *defs, | ||||
unsigned int defcount, FILE *io) | ||||
{ | ||||
const MOJOSHADER_parseData *pd; | ||||
int retval = 0; | ||||
pd = MOJOSHADER_assemble(fname, buf, len, NULL, 0, NULL, 0, | ||||
defs, defcount, open_include, close_include, | ||||
Malloc, Free, NULL); | ||||
if (pd->error_count > 0) | ||||
{ | ||||
int i; | ||||
for (i = 0; i < pd->error_count; i++) | ||||
{ | ||||
fprintf(stderr, "%s:%d: ERROR: %s\n", | ||||
pd->errors[i].filename ? pd->errors[i].filename : "???", | ||||
pd->errors[i].error_position, | ||||
pd->errors[i].error); | ||||
} // for | ||||
} // if | ||||
else | ||||
{ | ||||
if (pd->output != NULL) | ||||
{ | ||||
const int len = pd->output_len; | ||||
if ((len) && (fwrite(pd->output, len, 1, io) != 1)) | ||||
printf(" ... fwrite('%s') failed.\n", outfile); | ||||
else if ((outfile != NULL) && (fclose(io) == EOF)) | ||||
printf(" ... fclose('%s') failed.\n", outfile); | ||||
else | ||||
retval = 1; | ||||
} // if | ||||
} // else | ||||
MOJOSHADER_freeParseData(pd); | ||||
return retval; | ||||
} // assemble | ||||
static int ast(const char *fname, const char *buf, int len, | ||||
const char *outfile, const MOJOSHADER_preprocessorDefine *defs, | ||||
unsigned int defcount, FILE *io) | ||||
{ | ||||
const MOJOSHADER_astData *ad; | ||||
int retval = 0; | ||||
ad = MOJOSHADER_parseAst(MOJOSHADER_SRC_PROFILE_HLSL_PS_1_1, // !!! FIXME | ||||
fname, buf, len, defs, defcount, | ||||
open_include, close_include, Malloc, Free, NULL); | ||||
if (ad->error_count > 0) | ||||
{ | ||||
int i; | ||||
for (i = 0; i < ad->error_count; i++) | ||||
{ | ||||
fprintf(stderr, "%s:%d: ERROR: %s\n", | ||||
ad->errors[i].filename ? ad->errors[i].filename : "???", | ||||
ad->errors[i].error_position, | ||||
ad->errors[i].error); | ||||
} // for | ||||
} // if | ||||
else | ||||
{ | ||||
print_ast(io, 0, ad->ast); | ||||
if ((outfile != NULL) && (fclose(io) == EOF)) | ||||
printf(" ... fclose('%s') failed.\n", outfile); | ||||
else | ||||
retval = 1; | ||||
} // else | ||||
MOJOSHADER_freeAstData(ad); | ||||
return retval; | ||||
} // ast | ||||
static int compile(const char *fname, const char *buf, int len, | ||||
const char *outfile, | ||||
const MOJOSHADER_preprocessorDefine *defs, | ||||
unsigned int defcount, FILE *io) | ||||
{ | ||||
// !!! FIXME: write me. | ||||
//const MOJOSHADER_parseData *pd; | ||||
//int retval = 0; | ||||
MOJOSHADER_compile(MOJOSHADER_SRC_PROFILE_HLSL_PS_1_1, // !!! FIXME | ||||
fname, buf, len, defs, defcount, | ||||
open_include, close_include, | ||||
Malloc, Free, NULL); | ||||
return 1; | ||||
} // compile | ||||
typedef enum | ||||
{ | ||||
ACTION_UNKNOWN, | ||||
ACTION_VERSION, | ||||
ACTION_PREPROCESS, | ||||
ACTION_ASSEMBLE, | ||||
ACTION_AST, | ||||
ACTION_COMPILE, | ||||
} Action; | ||||
int main(int argc, char **argv) | ||||
{ | ||||
Action action = ACTION_UNKNOWN; | ||||
int retval = 1; | ||||
const char *infile = NULL; | ||||
const char *outfile = NULL; | ||||
int i; | ||||
MOJOSHADER_preprocessorDefine *defs = NULL; | ||||
unsigned int defcount = 0; | ||||
include_paths = (const char **) malloc(sizeof (char *)); | ||||
include_paths[0] = "."; | ||||
include_path_count = 1; | ||||
// !!! FIXME: clean this up. | ||||
for (i = 1; i < argc; i++) | ||||
{ | ||||
const char *arg = argv[i]; | ||||
if (strcmp(arg, "-P") == 0) | ||||
{ | ||||
if ((action != ACTION_UNKNOWN) && (action != ACTION_PREPROCESS)) | ||||
fail("Multiple actions specified"); | ||||
action = ACTION_PREPROCESS; | ||||
} // if | ||||
else if (strcmp(arg, "-A") == 0) | ||||
{ | ||||
if ((action != ACTION_UNKNOWN) && (action != ACTION_ASSEMBLE)) | ||||
fail("Multiple actions specified"); | ||||
action = ACTION_ASSEMBLE; | ||||
} // else if | ||||
else if (strcmp(arg, "-T") == 0) | ||||
{ | ||||
if ((action != ACTION_UNKNOWN) && (action != ACTION_AST)) | ||||
fail("Multiple actions specified"); | ||||
action = ACTION_AST; | ||||
} // else if | ||||
else if (strcmp(arg, "-C") == 0) | ||||
{ | ||||
if ((action != ACTION_UNKNOWN) && (action != ACTION_COMPILE)) | ||||
fail("Multiple actions specified"); | ||||
action = ACTION_COMPILE; | ||||
} // else if | ||||
else if ((strcmp(arg, "-V") == 0) || (strcmp(arg, "--version") == 0)) | ||||
{ | ||||
if ((action != ACTION_UNKNOWN) && (action != ACTION_VERSION)) | ||||
fail("Multiple actions specified"); | ||||
action = ACTION_VERSION; | ||||
} // else if | ||||
else if (strcmp(arg, "-o") == 0) | ||||
{ | ||||
if (outfile != NULL) | ||||
fail("multiple output files specified"); | ||||
arg = argv[++i]; | ||||
if (arg == NULL) | ||||
fail("no filename after '-o'"); | ||||
outfile = arg; | ||||
} // if | ||||
else if (strcmp(arg, "-I") == 0) | ||||
{ | ||||
arg = argv[++i]; | ||||
if (arg == NULL) | ||||
fail("no path after '-I'"); | ||||
include_paths = (const char **) realloc(include_paths, | ||||
(include_path_count+1) * sizeof (char *)); | ||||
include_paths[include_path_count] = arg; | ||||
include_path_count++; | ||||
} // if | ||||
else if (strncmp(arg, "-D", 2) == 0) | ||||
{ | ||||
arg += 2; | ||||
char *ident = strdup(arg); | ||||
char *ptr = strchr(ident, '='); | ||||
const char *val = ""; | ||||
if (ptr) | ||||
{ | ||||
*ptr = '\0'; | ||||
val = ptr+1; | ||||
} // if | ||||
defs = (MOJOSHADER_preprocessorDefine *) realloc(defs, | ||||
(defcount+1) * sizeof (MOJOSHADER_preprocessorDefine)); | ||||
defs[defcount].identifier = ident; | ||||
defs[defcount].definition = val; | ||||
defcount++; | ||||
} // else if | ||||
else | ||||
{ | ||||
if (infile != NULL) | ||||
fail("multiple input files specified"); | ||||
infile = arg; | ||||
} // else | ||||
} // for | ||||
if (action == ACTION_UNKNOWN) | ||||
action = ACTION_ASSEMBLE; | ||||
if (action == ACTION_VERSION) | ||||
{ | ||||
printf("mojoshader-compiler, changeset %s\n", MOJOSHADER_CHANGESET); | ||||
return 0; | ||||
} // if | ||||
if (infile == NULL) | ||||
fail("no input file specified"); | ||||
FILE *io = fopen(infile, "rb"); | ||||
if (io == NULL) | ||||
fail("failed to open input file"); | ||||
fseek(io, 0, SEEK_END); | ||||
long fsize = ftell(io); | ||||
fseek(io, 0, SEEK_SET); | ||||
if (fsize == -1) | ||||
fsize = 1000000; | ||||
char *buf = (char *) malloc(fsize); | ||||
const int rc = fread(buf, 1, fsize, io); | ||||
fclose(io); | ||||
if (rc == EOF) | ||||
fail("failed to read input file"); | ||||
FILE *outio = outfile ? fopen(outfile, "wb") : stdout; | ||||
if (outio == NULL) | ||||
fail("failed to open output file"); | ||||
if (action == ACTION_PREPROCESS) | ||||
retval = (!preprocess(infile, buf, rc, outfile, defs, defcount, outio)); | ||||
else if (action == ACTION_ASSEMBLE) | ||||
retval = (!assemble(infile, buf, rc, outfile, defs, defcount, outio)); | ||||
else if (action == ACTION_AST) | ||||
retval = (!ast(infile, buf, rc, outfile, defs, defcount, outio)); | ||||
else if (action == ACTION_COMPILE) | ||||
retval = (!compile(infile, buf, rc, outfile, defs, defcount, outio)); | ||||
if ((retval != 0) && (outfile != NULL)) | ||||
remove(outfile); | ||||
free(buf); | ||||
for (i = 0; i < defcount; i++) | ||||
free((void *) defs[i].identifier); | ||||
free(defs); | ||||
free(include_paths); | ||||
return retval; | ||||
} // main | ||||
// end of mojoshader-compiler.c ... | ||||