Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
branch: master
Fetching contributors…

Cannot retrieve contributors at this time

353 lines (279 sloc) 8.339 kb
/*
* Part of ntpl
* ntpl.native v.0.3.0
* Copyright 2010, Fedor Indutny
* Released under MIT license
*/
#include <v8.h>
#include <stdio.h> // sprintf
#include <string.h> // strlen
#include "ntpl.mod.h"
using namespace v8;
#ifdef NODE_NTPL_MODIFICATORS_MODULE
using namespace ntpl;
#endif //NODE_NTPL_MODIFICATORS_MODULE
namespace ntpl {
static Persistent<String> REPLACEMENTS_SYMBOL;
static Persistent<String> REPLVARS_SYMBOL;
static Persistent<String> CODE_SYMBOL;
static Persistent<String> OPTIONS_SYMBOL;
// Parser states
enum PARSER_STATE {
STAND_BY = 0,
BRACES_MODIFICATOR = 1,
BRACES = 2,
COMMENT_BRACES = 3,
OPTIONS_BRACES = 4
};
// Here will be replacements
struct Replacements_ {
Local<Array> replacements;
Local<Array> replVars;
int replacements_count;
};
typedef struct Replacements_ Replacements;
// Will hold parser position
struct Position_ {
int last;
int current;
Local<String> modificator;
Local<Array> code;
int codePosition;
Local<Object> namespace_;
Local<Object> options;
unsigned char* input;
};
typedef struct Position_ Position;
static inline Replacements* new_replacements()
{
Replacements* result = new Replacements;
result->replacements_count = 0;
return result;
}
static inline Position* new_position(unsigned char* input)
{
Position* result = new Position;
result->current = 0;
result->last = 0;
result->codePosition = 0;
result->input = input;
return result;
}
static inline Local<String> getInputPart( Position* pos)
{
HandleScope scope;
// Get part of input using parser's last pos and current pos
Local<String> result = String::New( (char*) pos->input + pos->last, pos->current - pos->last );
return scope.Close(result);
}
static inline Local<String> getInputSymbolPart( Position* pos)
{
HandleScope scope;
// Get part of input using parser's last pos and current pos
Local<String> result = String::NewSymbol( (char*) pos->input + pos->last, pos->current - pos->last );
return scope.Close(result);
}
static Local<String> callModificator( Position* pos, Local<Object> modificators )
{
HandleScope scope;
// Get code from input
Local<String> code = getInputPart( pos );
// If modificator is not empty && "modificators" has this property
if (pos->modificator->Length() && modificators->Has(pos->modificator))
{
// Get properties' value
Local<Value> tmp = modificators->Get(pos->modificator);
// If not function - return unprocessed code or modificator's value
// (note, that modificator in this case is part of code too!)
if (!tmp->IsFunction())
{
// If not undefined - return modificator's value
if (!tmp->IsUndefined())
{
return scope.Close(tmp->ToString());
}
else
{
return scope.Close(String::Concat(pos->modificator, code));
}
}
// Get function
Local<Function> x = Local<Function>::Cast(tmp);
// Create arguments:
// * code
// * namespace
Local<Value> argv[3] = { code , pos->namespace_ , pos->options};
// Call it & return String value
return scope.Close(x->Call(modificators, 3, argv)->ToString());
} else
{
return scope.Close(String::Concat(pos->modificator, code));
}
}
static void pushVariable( Position* pos, Replacements* replace)
{
HandleScope scope;
Local<String> var_value = getInputPart(pos);
// If not empty
if (!var_value->Length())
return;
// Add replacement var
replace->replacements->Set(
replace->replacements_count,
var_value
);
// Add r to replVars
// Will be joined in javascript
replace->replVars->Set(
replace->replacements_count,
Integer::New(replace->replacements_count)
);
// Create buffer for output
char* var_num = new char[28];
// Templating
sprintf(var_num, "$p($%d,$_);", replace->replacements_count);
// Increment count
replace->replacements_count++;
// Push template into code
pos->code->Set(pos->codePosition++, String::New( var_num ));
}
bool parseValidateArgs(const Arguments& args)
{
// Three arguments must be passed:
// * Input string
// * Modificators object
// * Namespace object
// * Options object
if ((args.Length() < 3) ||
(!args[0]->IsString()) ||
(!args[1]->IsObject()) ||
(!args[2]->IsObject()) ||
(!args[3]->IsObject()))
{
return false;
}
return true;
}
#define PARSER_OP(offset, char) (pos->input[pos->current+offset] == char)
#define PARSER_OP2(tok) (pos->input[pos->current] == tok[0] && pos->input[pos->current+1] == tok[1])
#define PARSER_MOVE(offset) pos->last=(pos->current+=offset)
#define PARSER_OP_SPACES(offset) (PARSER_OP(0,' ') || PARSER_OP(0,'\t'))
Handle<Value> parse(const Arguments& args)
{
HandleScope scope;
Local<Object> result = Object::New();
// Validate arguments
if (!parseValidateArgs(args))
return scope.Close(result);
// Parse arguments
String::Utf8Value inputData(args[0]->ToString());
Local<Object> modificators = Local<Object>::Cast(args[1]);
// Prepare parser vars
Replacements* replace = new_replacements();
replace->replacements = Array::New();
replace->replVars = Array::New();
// Set parser pos
Position* pos = new_position((unsigned char*) *inputData);
pos->modificator = String::NewSymbol("");
pos->code = Array::New();
// Include namespace into position
pos->namespace_ = Local<Object>::Cast(args[2]);
// Create options object
pos->options = Local<Object>::Cast(args[3]);
PARSER_STATE state = STAND_BY;
while( pos->input[pos->current] )
{
// Open code-braces {% ... %}
if (state == STAND_BY && PARSER_OP2("{%"))
{
// Push all that was before
pushVariable(pos, replace);
// Change state & pos
state = BRACES_MODIFICATOR;
PARSER_MOVE(2);
}
// Catch modificator {%modificator ... %}
else if (state == BRACES_MODIFICATOR && PARSER_OP_SPACES(0))
{
// Get modificator
pos->modificator = getInputSymbolPart(pos);
// Change state & pos
state = BRACES;
PARSER_MOVE(0);
}
// Close code-braces
else if ((state == BRACES || state == BRACES_MODIFICATOR)
&& PARSER_OP2("%}"))
{
// Case: {%modificator_name%}
// State will be BRACES_MODIFICATOR
// But %} will be found
if (state == BRACES_MODIFICATOR) {
// Get modificator && changes pos
pos->modificator = getInputSymbolPart(pos);
PARSER_MOVE(0);
}
Local<String> code_piece = callModificator(pos, modificators);
// If not empty
if (code_piece->Length())
pos->code->Set(pos->codePosition++, code_piece);
// Change state & pos
state = STAND_BY;
PARSER_MOVE(2);
}
// Open comment braces {* ... *}
else if (state == STAND_BY && PARSER_OP2("{*"))
{
// Push all that was before
pushVariable(pos, replace);
// Change state & pos
state = COMMENT_BRACES;
PARSER_MOVE(2);
}
// Close comment braces
else if (state == COMMENT_BRACES && PARSER_OP2("*}"))
{
// Change state & pos
state = STAND_BY;
PARSER_MOVE(2);
}
// Skip symbols
else
{
// Change pos
pos->current++;
}
}
// Process terminating characters
if (state == STAND_BY) {
pushVariable( pos, replace);
}
// Set output keys
result->Set( REPLACEMENTS_SYMBOL, replace->replacements );
result->Set( REPLVARS_SYMBOL, replace->replVars );
result->Set( CODE_SYMBOL, pos->code );
// Avoid memory leaks
delete replace;
delete pos;
return scope.Close(result);
}
#undef PARSER_OP
#undef PARSER_OP2
#undef PARSER_MOVE
#undef PARSER_OP_SPACES
#define EXPOSE_FUNC(label, func) target->Set(String::NewSymbol(label), func)
#define PERS_LABEL(label) Persistent<String>::New(String::NewSymbol(label))
extern "C" void init (Handle<Object> target)
{
HandleScope scope;
REPLACEMENTS_SYMBOL = PERS_LABEL("replacements");
CODE_SYMBOL = PERS_LABEL("code");
REPLVARS_SYMBOL = PERS_LABEL("replVars");
#ifdef NODE_NTPL_MODIFICATORS_MODULE
mod::init(target);
#endif //NODE_NTPL_MODIFICATORS_MODULE
EXPOSE_FUNC("parse",FunctionTemplate::New(parse)->GetFunction());
}
#undef EXPOSE_FUNC
#undef PERS_LABEL
}
Jump to Line
Something went wrong with that request. Please try again.