Commit 8744e0ca authored by platypro's avatar platypro

Added optional init.lua script, for setting variables when special properties...

Added optional init.lua script, for setting variables when special properties are not enough. Can also be used for error-checking options.
parent a07562dc
{
"name" : "Test",
"type" : "static",
"calculator" : "banned",
"hints": ["Test hint 1", "Test hint 2"],
"templates":[ {
......
......@@ -43,6 +43,12 @@
snprintf(buf, FILENAME_MAX, "%s/%s/exercise.json", proj, id);\
} while(0);
#define GET_EXERCISE_INIT(buf, proj, id) do { \
size_t buf##_len = strlen(id) + strlen(proj) + 15; \
buf = alloca(buf##_len); \
snprintf(buf, FILENAME_MAX, "%s/%s/init.lua", proj, id);\
} while(0);
#define GET_EXERCISE_SCRIPT(buf, proj, id) do { \
size_t buf##_len = strlen(id) + strlen(proj) + 15; \
buf = alloca(buf##_len); \
......@@ -276,7 +282,7 @@ EXERCISE* exercise_load(PG_STATE* state, char* exerId)
}
}
if(exer->hasScript && !script_load(state, exerId, scriptPath))
if(exer->hasScript && !script_load(state, exer->id, scriptPath))
{
WJRCloseDocument(exer_rdr);
return NULL;
......@@ -336,30 +342,42 @@ uint32_t pset_loadQuestions(PG_STATE* state, EXERCISEREF** base, WJReader r, cha
WJEMergeObjects(exref->options, exer->baseOptions, false);
WJEMergeObjects(exref->options, WJEObject(question, KEYLIST(KEY_OPTION), WJE_GET), true);
if(exer->variables)
char* initpath = NULL;
GET_EXERCISE_INIT(initpath, state->project->path_exercise, exref->exercise->id);
if(!access(initpath, F_OK))
{
if(!script_runInit(state, exref, initpath))
return 0;
}
else if(exer->variables)
{
uint32_t randmax = 1;
exref->instancedVariables = calloc(exer->numVariables, sizeof(EXERCISEVARIABLE));
exref->numInstancedVariables = exer->numVariables;
exref->instancedVariables = calloc(exref->numInstancedVariables, sizeof(EXERCISEVARIABLE));
EXERCISEVARIABLE* variable_out = exref->instancedVariables;
_EXERCISEVARIABLE* variable_in = exer->variables;
while(variable_in)
{
variable_out->name = variable_in->name;
if(variable_in->value.type == PSTYPE_INTEGER)
variable_out->value = variable_in->value.unit1.intValue;
else sp_evaluate_number(&variable_in->value, SP_SCOPE_EREF(exref), &variable_out->value);
sp_evaluate_number(&variable_in->value, SP_SCOPE_EREF(exref), &variable_out->value);
variable_in = variable_in->next;
randmax *= variable_out->value;
variable_out++;
}
// Initalize unirand
unirand_seed(&exref->rand, randmax);
}
uint32_t randmax = 1;
for(int i = exref->numInstancedVariables; i; i--)
{
if(exref->instancedVariables[i - 1].value > 0)
randmax *= exref->instancedVariables[i - 1].value;
}
// Initalize unirand
unirand_seed(&exref->rand, randmax);
exref ++;
}
return numQuestions;
......@@ -464,7 +482,7 @@ GENMODE exercise_toGenMode(char* name)
EXERCISEVARIABLE* exercise_getVariable(QUESTION* q, char* id)
{
EXERCISEVARIABLE* variable = q->eref->instancedVariables;
uint32_t variablesLeft = q->eref->exercise->numVariables;
uint32_t variablesLeft = q->eref->numInstancedVariables;
while(variablesLeft)
{
if(!strcmp(variable->name, id))
......
......@@ -93,6 +93,7 @@ typedef struct ExerciseRef
struct unirand_t rand;
EXERCISEVARIABLE* instancedVariables;
uint32_t numInstancedVariables;
} EXERCISEREF;
......
......@@ -25,7 +25,7 @@
void lua_pushJSON(lua_State* L, WJElement src);
WJElement getWJEBase(lua_State* L, int idx)
WJElement getTableBase(lua_State* L, int idx)
{
WJElement result;
// Get WJElement base from metatable
......@@ -39,7 +39,7 @@ WJElement getWJEBase(lua_State* L, int idx)
int jsonObj__len(lua_State* L)
{
WJElement base = getWJEBase(L, 1);
WJElement base = getTableBase(L, 1);
lua_pushnumber(L, base->count);
......@@ -48,7 +48,7 @@ int jsonObj__len(lua_State* L)
int jsonObj__index(lua_State* L)
{
WJElement base = getWJEBase(L, 1);
WJElement base = getTableBase(L, 1);
switch(lua_type(L, 2))
{
......@@ -353,7 +353,19 @@ void script_init(PG_STATE* script)
script->exercise.script.L = L;
}
uint8_t script_load(PG_STATE* scr, char* id, char* path)
static bool lua_loadFile_(lua_State* L, char* path)
{
int err;
if((err = luaL_loadfile (L, path)) != LUA_OK)
{
printf("Error in file %s: %s\n",
path, lua_tostring(L,-1));
return false;
}
return true;
}
bool script_load(PG_STATE* scr, char* id, char* path)
{
lua_State* L = scr->exercise.script.L;
bool result = true;
......@@ -361,15 +373,98 @@ uint8_t script_load(PG_STATE* scr, char* id, char* path)
//Load the function into the function table
lua_getfield(L, LUA_REGISTRYINDEX, LUA_KEY_SCRIPTS);
int err;
if((err = luaL_loadfile (L, path)) != LUA_OK)
result = lua_loadFile_(L, path);
if(result)
lua_setfield(L, -2, id);
lua_pop(L, 1);
return result;
}
bool script_runInit(struct PG_State* script, EXERCISEREF* exref, char* path)
{
bool result = true;
lua_State* L = script->exercise.script.L;
result = lua_loadFile_(L, path);
if(result)
{
lua_pushJSON(L, exref->options);
lua_setglobal(L, KEYLIST(KEY_OPTION));
lua_pushJSON(L, exref->exercise->staticStore);
lua_setglobal(L, KEY_STATIC);
// Push variable table
lua_newtable(L);
if(exref->exercise->variables)
{
_EXERCISEVARIABLE* variable_in = exref->exercise->variables;
while(variable_in)
{
uint32_t val = 0;
if(!sp_evaluate_number(&variable_in->value, SP_SCOPE_EREF(exref), &val))
return false;
lua_pushstring(L, variable_in->name);
lua_pushinteger(L, val);
lua_settable(L, -3);
variable_in = variable_in->next;
}
}
lua_setglobal(L, KEYLIST(KEY_VARIABLE));
}
if(lua_pcall(L, 0, LUA_MULTRET, 0) != LUA_OK)
{
printf("%s\n", lua_tostring(L,-1));
printf("Initialization error in exercise %s: %s\n", exref->exercise->id, lua_tostring(L, -1));
result = false;
}
if(result)
lua_setfield(L, -2, id);
{
// Populate variables
lua_getglobal(L, KEYLIST(KEY_VARIABLE));
// Get variable count
exref->numInstancedVariables = 0;
lua_pushnil(L);
while (lua_next(L, -2) != 0)
{
if(lua_type(L, -2) != LUA_TSTRING)
{
printf("Initialization error in exercise %s: Numeric index used as variable ID!\n",
exref->exercise->id);
return false;
}
if(lua_type(L, -1) != LUA_TNUMBER)
{
printf("Initialization error in exercise %s: Variable %s is not a number!\n",
exref->exercise->id, lua_tostring(L, -2));
return false;
}
exref->numInstancedVariables ++;
lua_pop(L, 1);
}
// Allocate space, then load values
exref->instancedVariables = calloc(exref->numInstancedVariables, sizeof(EXERCISEVARIABLE));
int at = 0;
lua_pushnil(L);
while (lua_next(L, -2) != 0)
{
exref->instancedVariables[at].name = (char*) lua_tostring(L, -2);
exref->instancedVariables[at].value = lua_tonumber(L, -1);
lua_pop(L, 1);
at++;
}
}
lua_pop(L, 1);
return result;
......@@ -404,12 +499,12 @@ bool script_genQuiz(PG_STATE* scr, QUESTION* question, EXERCISEREF* exref, uint3
lua_setfield(L, LUA_REGISTRYINDEX, LUA_KEY_STATE);
//Push question variables
if(exref->exercise->numVariables)
if(exref->numInstancedVariables)
{
uint32_t varat = 0;
uint32_t multiplier = 1;
lua_createtable(L, exref->exercise->numVariables, 0);
while(varat < exref->exercise->numVariables)
lua_createtable(L, exref->numInstancedVariables, 0);
while(varat < exref->numInstancedVariables)
{
uint32_t maxval = exref->instancedVariables[varat].value;
uint32_t val = ceil((questionID % (multiplier * maxval)) / multiplier);
......@@ -425,33 +520,25 @@ bool script_genQuiz(PG_STATE* scr, QUESTION* question, EXERCISEREF* exref, uint3
lua_setglobal(L, KEYLIST(KEY_VARIABLE));
}
//Load function table and copy out the function
lua_getfield(L, LUA_REGISTRYINDEX, LUA_KEY_SCRIPTS);
lua_getfield(L, -1, exref->exercise->id);
//Set up scope and map existing globals to it
lua_newtable(L);
lua_newtable(L);
lua_getglobal(L, "_G");
lua_setfield(L, -2, "__index");
lua_setmetatable(L, -2);
lua_pushJSON(L, exref->options);
lua_setfield(L, -2, KEYLIST(KEY_OPTION));
lua_setglobal(L, KEYLIST(KEY_OPTION));
lua_pushJSON(L, exref->exercise->staticStore);
lua_setfield(L, -2, KEY_STATIC);
lua_setupvalue(L, -2, 1);
lua_setglobal(L, KEY_STATIC);
// Seed with question ID
srand(questionID);
//Load function table and copy out the function
lua_getfield(L, LUA_REGISTRYINDEX, LUA_KEY_SCRIPTS);
lua_getfield(L, -1, exref->exercise->id);
if(lua_pcall(L, 0, LUA_MULTRET,0) != LUA_OK)
{
printf("Lua Error in exercise %s: %s\n", exref->exercise->id, lua_tostring(L, -1));
result = false;
}
//Pop function table
lua_pop(L, 1);
return result;
......
......@@ -35,7 +35,8 @@ typedef struct PG_Script
lua_State* L;
} PG_SCRIPT;
extern uint8_t script_load(struct PG_State* script, char* id, char* path);
extern bool script_load(struct PG_State* script, char* id, char* path);
extern bool script_runInit(struct PG_State* script, struct ExerciseRef* exref, char* path);
bool script_unload(struct PG_State* scr, char* id);
extern void script_init(struct PG_State* script);
extern bool script_genQuiz(struct PG_State* script,
......
......@@ -5,9 +5,7 @@
"properties": [
{
"name": "choices",
"description": "A 0-based array containing the chices available to the widget. \
This list should be displayed in order to the user. \
If the element is SVG, use either SVG special property.",
"description": "A 0-based array containing the chices available to the widget.\nThis list should be displayed in order to the user.\nIf the element is SVG, use either SVG special property.",
"type": "array",
"properties": [
{
......@@ -30,8 +28,7 @@ If the element is SVG, use either SVG special property.",
},
{
"name": "marks",
"description": "If zero, this widget is worth marks for each correct answer. Otherwise,
this amount of marks are awarded only if all correct answers are selected.",
"description": "If zero, this widget is worth marks for each correct answer. Otherwise, this amount of marks are awarded only if all correct answers are selected.",
"default": 0,
"type": "number"
}]
......
......@@ -5,8 +5,7 @@
"properties": [
{
"name": "length",
"description": "The size of the input. Scale of this value is decided by clients,"
"but usually corresponds to the number of lines displayed.",
"description": "The size of the input. Scale of this value is decided by clients, but usually corresponds to the number of lines displayed.",
"default": 2,
"type": "string"
},
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment