App-SocialCalc-Multiplayer
view release on metacpan or search on metacpan
socialcalc/formula1.js view on Meta::CPAN
SocialCalc.Formula.TokenType = {num: 1, coord: 2, op: 3, name: 4, error: 5, string: 6, space: 7};
SocialCalc.Formula.CharClass = {num: 1, numstart: 2, op: 3, eof: 4, alpha: 5, incoord: 6, error: 7, quote: 8, space: 9, specialstart: 10};
SocialCalc.Formula.CharClassTable = {
" ": 9, "!": 3, '"': 8, "#": 10, "$":6, "%":3, "&":3, "(": 3, ")": 3, "*": 3, "+": 3, ",": 3, "-": 3, ".": 2, "/": 3,
"0": 1, "1": 1, "2": 1, "3": 1, "4": 1, "5": 1, "6": 1, "7": 1, "8": 1, "9": 1,
":": 3, "<": 3, "=": 3, ">": 3,
"A": 5, "B": 5, "C": 5, "D": 5, "E": 5, "F": 5, "G": 5, "H": 5, "I": 5, "J": 5, "K": 5, "L": 5, "M": 5, "N": 5,
"O": 5, "P": 5, "Q": 5, "R": 5, "S": 5, "T": 5, "U": 5, "V": 5, "W": 5, "X": 5, "Y": 5, "Z": 5,
"^": 3, "_": 5,
"a": 5, "b": 5, "c": 5, "d": 5, "e": 5, "f": 5, "g": 5, "h": 5, "i": 5, "j": 5, "k": 5, "l": 5, "m": 5, "n": 5,
"o": 5, "p": 5, "q": 5, "r": 5, "s": 5, "t": 5, "u": 5, "v": 5, "w": 5, "x": 5, "y": 5, "z": 5
};
SocialCalc.Formula.UpperCaseTable = {
"a": "A", "b": "B", "c": "C", "d": "D", "e": "E", "f": "F", "g": "G", "h": "H", "i": "I", "j": "J", "k": "K", "l": "L", "m": "M",
"n": "N", "o": "O", "p": "P", "q": "Q", "r": "R", "s": "S", "t": "T", "u": "U", "v": "V", "w": "W", "x": "X", "y": "Y", "z": "Z"
}
SocialCalc.Formula.SpecialConstants = { // names that turn into constants for name lookup
"#NULL!": "0,e#NULL!", "#NUM!": "0,e#NUM!", "#DIV/0!": "0,e#DIV/0!", "#VALUE!": "0,e#VALUE!",
"#REF!": "0,e#REF!", "#NAME?": "0,e#NAME?"};
// Operator Precedence table
//
// 1- !, 2- : ,, 3- M P, 4- %, 5- ^, 6- * /, 7- + -, 8- &, 9- < > = G(>=) L(<=) N(<>),
// Negative value means Right Associative
SocialCalc.Formula.TokenPrecedence = {
"!": 1,
":": 2, ",": 2,
"M": -3, "P": -3,
"%": 4,
"^": 5,
"*": 6, "/": 6,
"+": 7, "-": 7,
"&": 8,
"<": 9, ">": 9, "G": 9, "L": 9, "N": 9
};
// Convert one-char token text to input text:
SocialCalc.Formula.TokenOpExpansion = {'G': '>=', 'L': '<=', 'M': '-', 'N': '<>', 'P': '+'};
//
// Information about the resulting value types when doing operations on values (used by LookupResultType)
//
// Each object entry is an object with specific types with result type info as follows:
//
// 'type1a': '|type2a:resulta|type2b:resultb|...
// Type of t* or n* matches any of those types not listed
// Results may be a type or the numbers 1 or 2 specifying to return type1 or type2
SocialCalc.Formula.TypeLookupTable = {
unaryminus: { 'n*': '|n*:1|', 'e*': '|e*:1|', 't*': '|t*:e#VALUE!|', 'b': '|b:n|'},
unaryplus: { 'n*': '|n*:1|', 'e*': '|e*:1|', 't*': '|t*:e#VALUE!|', 'b': '|b:n|'},
unarypercent: { 'n*': '|n:n%|n*:n|', 'e*': '|e*:1|', 't*': '|t*:e#VALUE!|', 'b': '|b:n|'},
plus: {
'n%': '|n%:n%|nd:n|nt:n|ndt:n|n$:n|n:n|n*:n|b:n|e*:2|t*:e#VALUE!|',
'nd': '|n%:n|nd:nd|nt:ndt|ndt:ndt|n$:n|n:nd|n*:n|b:n|e*:2|t*:e#VALUE!|',
'nt': '|n%:n|nd:ndt|nt:nt|ndt:ndt|n$:n|n:nt|n*:n|b:n|e*:2|t*:e#VALUE!|',
'ndt': '|n%:n|nd:ndt|nt:ndt|ndt:ndt|n$:n|n:ndt|n*:n|b:n|e*:2|t*:e#VALUE!|',
'n$': '|n%:n|nd:n|nt:n|ndt:n|n$:n$|n:n$|n*:n|b:n|e*:2|t*:e#VALUE!|',
'nl': '|n%:n|nd:n|nt:n|ndt:n|n$:n|n:n|n*:n|b:n|e*:2|t*:e#VALUE!|',
'n': '|n%:n|nd:nd|nt:nt|ndt:ndt|n$:n$|n:n|n*:n|b:n|e*:2|t*:e#VALUE!|',
'b': '|n%:n%|nd:nd|nt:nt|ndt:ndt|n$:n$|n:n|n*:n|b:n|e*:2|t*:e#VALUE!|',
't*': '|n*:e#VALUE!|t*:e#VALUE!|b:e#VALUE!|e*:2|',
'e*': '|e*:1|n*:1|t*:1|b:1|'
},
concat: {
't': '|t:t|th:th|tw:tw|tl:t|t*:2|e*:2|',
'th': '|t:th|th:th|tw:t|tl:th|t*:t|e*:2|',
'tw': '|t:tw|th:t|tw:tw|tl:tw|t*:t|e*:2|',
'tl': '|t:tl|th:th|tw:tw|tl:tl|t*:t|e*:2|',
'e*': '|e*:1|n*:1|t*:1|'
},
oneargnumeric: { 'n*': '|n*:n|', 'e*': '|e*:1|', 't*': '|t*:e#VALUE!|', 'b': '|b:n|'},
twoargnumeric: { 'n*': '|n*:n|t*:e#VALUE!|e*:2|', 'e*': '|e*:1|n*:1|t*:1|', 't*': '|t*:e#VALUE!|n*:e#VALUE!|e*:2|'},
propagateerror: { 'n*': '|n*:2|e*:2|', 'e*': '|e*:2|', 't*': '|t*:2|e*:2|', 'b': '|b:2|e*:2|'}
};
/* *******************
parseinfo = SocialCalc.Formula.ParseFormulaIntoTokens(line)
Parses a text string as if it was a spreadsheet formula
This uses a simple state machine run on each character in turn.
States remember whether a number is being gathered, etc.
The result is parseinfo which is an array with one entry for each token:
parseinfo[i] = {
text: "the characters making up the parsed token",
type: the type of the token (a number),
opcode: a single character version of an operator suitable for use in the
precedence table and distinguishing between unary and binary + and -.
************************* */
SocialCalc.Formula.ParseFormulaIntoTokens = function(line) {
var i, ch, chclass, haddecimal, last_token, last_token_type, last_token_text, t;
var scf = SocialCalc.Formula;
var scc = SocialCalc.Constants;
var parsestate = scf.ParseState;
var tokentype = scf.TokenType;
var charclass = scf.CharClass;
var charclasstable = scf.CharClassTable;
var uppercasetable = scf.UpperCaseTable; // much faster than toUpperCase function
var pushtoken = scf.ParsePushToken;
var coordregex = /^\$?[A-Z]{1,2}\$?[1-9]\d*$/i;
var parseinfo = [];
var str = "";
var state = 0;
var haddecimal = false;
socialcalc/formula1.js view on Meta::CPAN
var format_number_for_display = SocialCalc.format_number_for_display || function(v, t, f) {return v+"";};
var errortext = "";
var function_start = -1;
var missingOperandError = {value: "", type: "e#VALUE!", error: scc.s_parseerrmissingoperand};
var operand = [];
var PushOperand = function(t, v) {operand.push({type: t, value: v});};
var i, rii, prii, ttype, ttext, value1, value2, tostype, tostype2, resulttype, valuetype, cond, vmatch, smatch;
if (!parseinfo.length || (! (revpolish instanceof Array))) {
return ({value: "", type: "e#VALUE!", error: (typeof revpolish == "string" ? revpolish : "")});
}
for (i=0; i<revpolish.length; i++) {
rii = revpolish[i];
if (rii == function_start) { // Remember the start of a function argument list
PushOperand("start", 0);
continue;
}
prii = parseinfo[rii];
ttype = prii.type;
ttext = prii.text;
if (ttype == tokentype.num) {
PushOperand("n", ttext-0);
}
else if (ttype == tokentype.coord) {
PushOperand("coord", ttext);
}
else if (ttype == tokentype.string) {
PushOperand("t", ttext);
}
else if (ttype == tokentype.op) {
if (operand.length <= 0) { // Nothing on the stack...
return missingOperandError;
break; // done
}
// Unary minus
if (ttext == 'M') {
value1 = operand_as_number(sheet, operand);
resulttype = lookup_result_type(value1.type, value1.type, typelookup.unaryminus);
PushOperand(resulttype, -value1.value);
}
// Unary plus
else if (ttext == 'P') {
value1 = operand_as_number(sheet, operand);
resulttype = lookup_result_type(value1.type, value1.type, typelookup.unaryplus);
PushOperand(resulttype, value1.value);
}
// Unary % - percent, left associative
else if (ttext == '%') {
value1 = operand_as_number(sheet, operand);
resulttype = lookup_result_type(value1.type, value1.type, typelookup.unarypercent);
PushOperand(resulttype, 0.01*value1.value);
}
// & - string concatenate
else if (ttext == '&') {
if (operand.length <= 1) { // Need at least two things on the stack...
return missingOperandError;
}
value2 = operand_as_text(sheet, operand);
value1 = operand_as_text(sheet, operand);
resulttype = lookup_result_type(value1.type, value1.type, typelookup.concat);
PushOperand(resulttype, value1.value + value2.value);
}
// : - Range constructor
else if (ttext == ':') {
if (operand.length <= 1) { // Need at least two things on the stack...
return missingOperandError;
}
value1 = scf.OperandsAsRangeOnSheet(sheet, operand); // get coords even if use name on other sheet
if (value1.error) { // not available
errortext = errortext || value1.error;
}
PushOperand(value1.type, value1.value); // push sheetname with range on that sheet
}
// ! - sheetname!coord
else if (ttext == '!') {
if (operand.length <= 1) { // Need at least two things on the stack...
return missingOperandError;
}
value1 = operands_as_coord_on_sheet(sheet, operand); // get coord even if name on other sheet
if (value1.error) { // not available
errortext = errortext || value1.error;
}
PushOperand(value1.type, value1.value); // push sheetname with coord or range on that sheet
}
// Comparison operators: < L = G > N (< <= = >= > <>)
else if (ttext == "<" || ttext == "L" || ttext == "=" || ttext == "G" || ttext == ">" || ttext == "N") {
if (operand.length <= 1) { // Need at least two things on the stack...
errortext = scc.s_parseerrmissingoperand; // remember error
break;
}
value2 = operand_value_and_type(sheet, operand);
value1 = operand_value_and_type(sheet, operand);
if (value1.type.charAt(0) == "n" && value2.type.charAt(0) == "n") { // compare two numbers
cond = 0;
if (ttext == "<") { cond = value1.value < value2.value ? 1 : 0; }
else if (ttext == "L") { cond = value1.value <= value2.value ? 1 : 0; }
else if (ttext == "=") { cond = value1.value == value2.value ? 1 : 0; }
else if (ttext == "G") { cond = value1.value >= value2.value ? 1 : 0; }
else if (ttext == ">") { cond = value1.value > value2.value ? 1 : 0; }
else if (ttext == "N") { cond = value1.value != value2.value ? 1 : 0; }
PushOperand("nl", cond);
}
( run in 2.492 seconds using v1.01-cache-2.11-cpan-ceb78f64989 )