├── .gitignore ├── test ├── specs │ ├── parser │ │ ├── parse │ │ │ ├── parser_parse_conjunction_test.js │ │ │ ├── parser_parse_disjunction_test.js │ │ │ ├── parser_parse_locationdata_test.js │ │ │ ├── parser_parse_implication_test.js │ │ │ ├── parser_parse_whitespace_test.js │ │ │ ├── parser_parse_word_test.js │ │ │ ├── parser_parse_equivalence_test.js │ │ │ ├── parser_parse_universalsent_test.js │ │ │ ├── parser_parse_existentialsent_test.js │ │ │ ├── parser_parse_comments_test.js │ │ │ ├── parser_parse_variable_test.js │ │ │ ├── parser_parse_stringliteral_test.js │ │ │ ├── parser_parse_sumo_test.js │ │ │ ├── parser_parse_numericliteral_test.js │ │ │ ├── parser_parse_exponent_test.js │ │ │ ├── parser_parse_negation_test.js │ │ │ ├── parser_parse_equation_test.js │ │ │ └── parser_parse_relsent_test.js │ │ ├── parser_interface_test.js │ │ └── extras │ │ │ ├── parser_extras_cb_test.js │ │ │ └── parser_extras_p_test.js │ └── ast_constructors │ │ ├── ast_node_constructor_test.js │ │ ├── ast_word_test.js │ │ ├── ast_kif_test.js │ │ ├── ast_equation_test.js │ │ ├── ast_conjunction_test.js │ │ ├── ast_disjunction_test.js │ │ ├── ast_equivalence_test.js │ │ ├── ast_negation_test.js │ │ ├── ast_numericliteral_test.js │ │ ├── ast_stringliteral_test.js │ │ ├── ast_variable_test.js │ │ ├── ast_implication_test.js │ │ ├── ast_relsent_test.js │ │ ├── ast_universalsent_test.js │ │ ├── ast_existentialsent_test.js │ │ └── ast_manifest_test.js └── resources │ └── sumo_core.kif ├── lib ├── ast_constructors │ ├── ast_constructor_base.js │ ├── wordnode.js │ ├── negationnode.js │ ├── numericliteralnode.js │ ├── conjunctionnode.js │ ├── disjunctionnode.js │ ├── kifnode.js │ ├── stringliteralnode.js │ ├── equationnode.js │ ├── implicationnode.js │ ├── variablenode.js │ ├── relsentnode.js │ ├── equivalencenode.js │ ├── universalsentnode.js │ ├── existentialsentnode.js │ └── ast_constructors.js ├── jkif_parser.js └── grammars.jison ├── package.json ├── LICENSE ├── CONTRIBUTING.md ├── README.md └── STYLE_GUIDE.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Dependencies 23 | node_modules 24 | 25 | # Users Environment Variables 26 | .lock-wscript 27 | -------------------------------------------------------------------------------- /test/specs/parser/parse/parser_parse_conjunction_test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'), 2 | expect = chai.expect, 3 | Parser = require('../../../../lib/jkif_parser'), 4 | ast = require('../../../../lib/ast_constructors/ast_constructors'); 5 | 6 | 7 | describe('Parser.parse Conjunction sentence parsing', function() { 8 | 9 | it('correctly parses a conjunction into a ConjunctionNode', function() { 10 | var parsed = Parser.parse('(AND arg1 arg2)').expressions[0]; 11 | expect(parsed).to.be.an.instanceof(ast.ConjunctionNode); 12 | expect(parsed.conjuncts).to.have.length(2); 13 | expect(parsed.conjuncts[0]).to.be.instanceof(ast.WordNode); 14 | }); 15 | 16 | }); 17 | -------------------------------------------------------------------------------- /test/specs/parser/parse/parser_parse_disjunction_test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'), 2 | expect = chai.expect, 3 | Parser = require('../../../../lib/jkif_parser'), 4 | ast = require('../../../../lib/ast_constructors/ast_constructors'); 5 | 6 | 7 | describe('Parser.parse Disjunction sentence parsing', function() { 8 | 9 | it('correctly parses a disjunction into a DisjunctionNode', function() { 10 | var parsed = Parser.parse('(or arg1 arg2)').expressions[0]; 11 | expect(parsed).to.be.an.instanceof(ast.DisjunctionNode); 12 | expect(parsed.disjuncts).to.have.length(2); 13 | expect(parsed.disjuncts[0]).to.be.instanceof(ast.WordNode); 14 | }); 15 | 16 | }); 17 | -------------------------------------------------------------------------------- /test/specs/parser/parse/parser_parse_locationdata_test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'), 2 | expect = chai.expect, 3 | Parser = require('../../../../lib/jkif_parser'), 4 | ast = require('../../../../lib/ast_constructors/ast_constructors'), 5 | locKeys = ['first_line', 'first_column', 'last_line', 'last_column'], 6 | parsed = Parser.parse('(exists (?F) (instance ?F Farmer))'); 7 | 8 | 9 | describe('Parser.parse Location Data', function() { 10 | 11 | it('should include location data in the parsed result', function() { 12 | expect(parsed.locationData).to.be.an.instanceof(Object); 13 | expect(parsed.locationData).to.have.all.keys(locKeys); 14 | }); 15 | 16 | }); 17 | -------------------------------------------------------------------------------- /lib/ast_constructors/ast_constructor_base.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jKif - 2015 3 | * ast_constructor_base.js 4 | * @file Base AST Node Constructor which all AST Nodes call up to on initialization 5 | * @author Clark Feusier - cfeusier@gmail.com 6 | * @copyright Copyright (C) Clark Feusier - All Rights Reserved 7 | */ 8 | 9 | 10 | /** 11 | * 12 | * @param {String} type 13 | * @param {Object} locationData 14 | * @constructor 15 | */ 16 | function ASTnodeConstructor(type, locationData) { 17 | this.type = type || 'ASTnodeConstructor'; 18 | this.locationData = locationData || {}; 19 | } 20 | 21 | 22 | /** 23 | * 24 | * @type {ASTnodeConstructor} 25 | */ 26 | module.exports = ASTnodeConstructor; 27 | -------------------------------------------------------------------------------- /lib/ast_constructors/wordnode.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jKif - 2015 3 | * wordnode.js 4 | * @file AST WordNode constructor for representing SUO-KIF words and identifiers 5 | * @author Clark Feusier - cfeusier@gmail.com 6 | * @copyright Copyright (C) Clark Feusier - All Rights Reserved 7 | */ 8 | 9 | 10 | var BaseNode = require('./ast_constructor_base'); 11 | 12 | 13 | /** 14 | * 15 | * @param {Object} locationData 16 | * @param {String} identifier 17 | * @constructor 18 | */ 19 | function WordNode(locationData, identifier) { 20 | BaseNode.call(this, 'WordNode', locationData); 21 | this.word = identifier; 22 | } 23 | 24 | 25 | /** 26 | * 27 | * @type {WordNode} 28 | */ 29 | module.exports = WordNode; 30 | -------------------------------------------------------------------------------- /test/specs/parser/parse/parser_parse_implication_test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'), 2 | expect = chai.expect, 3 | Parser = require('../../../../lib/jkif_parser'), 4 | ast = require('../../../../lib/ast_constructors/ast_constructors'); 5 | 6 | 7 | describe('Parser.parse Implication sentence parsing', function() { 8 | 9 | it('correctly parses an implication into an ImplicationNode', function () { 10 | var parsed = Parser.parse('(=> expr1 expr2)').expressions[0]; 11 | expect(parsed).to.be.an.instanceof(ast.ImplicationNode); 12 | expect(parsed.antecedent).to.be.an.instanceof(ast.WordNode); 13 | expect(parsed.consequent).to.be.instanceof(ast.WordNode); 14 | }); 15 | 16 | }); 17 | -------------------------------------------------------------------------------- /test/specs/parser/parse/parser_parse_whitespace_test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'), 2 | expect = chai.expect, 3 | Parser = require('../../../../lib/jkif_parser'), 4 | ast = require('../../../../lib/ast_constructors/ast_constructors'); 5 | 6 | 7 | describe('Parser.parse Whitespace', function() { 8 | 9 | it('correctly parses a single whitespace', function() { 10 | expect(Parser.parse(' ').expressions).to.be.empty; 11 | }); 12 | 13 | it('correctly parses multiple whitespaces in a row', function() { 14 | expect(Parser.parse(' ').expressions).to.be.empty; 15 | }); 16 | 17 | it('correctly parses a newline whitespace', function() { 18 | expect(Parser.parse('\n').expressions).to.be.empty; 19 | }); 20 | 21 | }); 22 | -------------------------------------------------------------------------------- /lib/ast_constructors/negationnode.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jKif - 2015 3 | * negationnode.js 4 | * @file AST NegationNode constructor for representing negations 5 | * @author Clark Feusier - cfeusier@gmail.com 6 | * @copyright Copyright (C) Clark Feusier - All Rights Reserved 7 | */ 8 | 9 | 10 | var BaseNode = require('./ast_constructor_base'); 11 | 12 | 13 | /** 14 | * 15 | * @param {Object} locationData 16 | * @param {ASTNode} negatedExpression 17 | * @constructor 18 | */ 19 | function NegationNode(locationData, negatedExpression) { 20 | BaseNode.call(this, 'NegationNode', locationData); 21 | this.negatedExpression = negatedExpression; 22 | } 23 | 24 | 25 | /** 26 | * 27 | * @type {NegationNode} 28 | */ 29 | module.exports = NegationNode; 30 | -------------------------------------------------------------------------------- /lib/ast_constructors/numericliteralnode.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jKif - 2015 3 | * numericliteralnode.js 4 | * @file AST NumericLiteralNode constructor for representing numeric literals 5 | * @author Clark Feusier - cfeusier@gmail.com 6 | * @copyright Copyright (C) Clark Feusier - All Rights Reserved 7 | */ 8 | 9 | 10 | var BaseNode = require('./ast_constructor_base'); 11 | 12 | 13 | /** 14 | * 15 | * @param {Object} locationData 16 | * @param {Number} rawNumber 17 | * @constructor 18 | */ 19 | function NumericLiteralNode(locationData, rawNumber) { 20 | BaseNode.call(this, 'NumericLiteralNode', locationData); 21 | this.number = +rawNumber; 22 | } 23 | 24 | 25 | /** 26 | * 27 | * @type {NumericLiteralNode} 28 | */ 29 | module.exports = NumericLiteralNode; 30 | -------------------------------------------------------------------------------- /test/specs/parser/parse/parser_parse_word_test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'), 2 | expect = chai.expect, 3 | Parser = require('../../../../lib/jkif_parser'), 4 | ast = require('../../../../lib/ast_constructors/ast_constructors'); 5 | 6 | 7 | describe('Parser.parse Word parsing', function() { 8 | 9 | it('correctly parses Words into WordNodes', function() { 10 | expect(Parser.parse('word').expressions[0]).to.be.an.instanceof(ast.WordNode); 11 | }); 12 | 13 | it('correctly parses a single Word', function() { 14 | expect(Parser.parse('word').expressions[0].word).to.equal('word'); 15 | }); 16 | 17 | it('correctly parses a multiple Words', function() { 18 | expect(Parser.parse('word secondword').expressions[1].word).to.equal('secondword'); 19 | }); 20 | 21 | }); 22 | -------------------------------------------------------------------------------- /lib/ast_constructors/conjunctionnode.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jKif - 2015 3 | * conjunctionnode.js 4 | * @file AST ConjunctionNode constructor for representing conjunctions 5 | * @author Clark Feusier - cfeusier@gmail.com 6 | * @copyright Copyright (C) Clark Feusier - All Rights Reserved 7 | */ 8 | 9 | 10 | var BaseNode = require('./ast_constructor_base'); 11 | 12 | 13 | /** 14 | * 15 | * @param {Object} locationData 16 | * @param {Array} conjuncts 17 | * @constructor 18 | */ 19 | function ConjunctionNode(locationData, conjuncts) { 20 | BaseNode.call(this, 'ConjunctionNode', locationData); 21 | this.conjuncts = this.conjuncts || []; 22 | this.conjuncts = this.conjuncts.concat(conjuncts); 23 | } 24 | 25 | 26 | /** 27 | * 28 | * @type {ConjunctionNode} 29 | */ 30 | module.exports = ConjunctionNode; 31 | -------------------------------------------------------------------------------- /lib/ast_constructors/disjunctionnode.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jKif - 2015 3 | * disjunctionnode.js 4 | * @file AST DisjunctionNode constructor for representing disjunctions 5 | * @author Clark Feusier - cfeusier@gmail.com 6 | * @copyright Copyright (C) Clark Feusier - All Rights Reserved 7 | */ 8 | 9 | 10 | var BaseNode = require('./ast_constructor_base'); 11 | 12 | 13 | /** 14 | * 15 | * @param {Object} locationData 16 | * @param {Array} disjuncts 17 | * @constructor 18 | */ 19 | function DisjunctionNode(locationData, disjuncts) { 20 | BaseNode.call(this, 'DisjunctionNode', locationData); 21 | this.disjuncts = this.disjuncts || []; 22 | this.disjuncts = this.disjuncts.concat(disjuncts); 23 | } 24 | 25 | 26 | /** 27 | * 28 | * @type {DisjunctionNode} 29 | */ 30 | module.exports = DisjunctionNode; 31 | -------------------------------------------------------------------------------- /lib/ast_constructors/kifnode.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jKif - 2015 3 | * kifnode.js 4 | * @file AST KIFNode constructor for representing the complete tree returned by jKif.Parser.parse 5 | * @author Clark Feusier - cfeusier@gmail.com 6 | * @copyright Copyright (C) Clark Feusier - All Rights Reserved 7 | */ 8 | 9 | 10 | var BaseNode = require('./ast_constructor_base'); 11 | 12 | 13 | /** 14 | * 15 | * @param {Object} locationData 16 | * @param {Array} kifExpressions 17 | * @constructor 18 | */ 19 | function KIFNode(locationData, kifExpressions) { 20 | BaseNode.call(this, 'KIFNode', locationData); 21 | this.expressions = this.expressions || []; 22 | this.expressions = this.expressions.concat(kifExpressions); 23 | } 24 | 25 | 26 | /** 27 | * 28 | * @type {KIFNode} 29 | */ 30 | module.exports = KIFNode; 31 | -------------------------------------------------------------------------------- /lib/ast_constructors/stringliteralnode.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jKif - 2015 3 | * stringliteralnode.js 4 | * @file AST StringLiteralNode constructor for representing string literals 5 | * @author Clark Feusier - cfeusier@gmail.com 6 | * @copyright Copyright (C) Clark Feusier - All Rights Reserved 7 | */ 8 | 9 | 10 | var BaseNode = require('./ast_constructor_base'); 11 | 12 | 13 | /** 14 | * 15 | * @param {Object} locationData 16 | * @param {String} rawString 17 | * @constructor 18 | */ 19 | function StringLiteralNode(locationData, rawString) { 20 | BaseNode.call(this, 'StringLiteralNode', locationData); 21 | this.rawString = rawString; 22 | this.chars = rawString.substring(1, rawString.length - 1); 23 | } 24 | 25 | 26 | /** 27 | * 28 | * @type {StringLiteralNode} 29 | */ 30 | module.exports = StringLiteralNode; 31 | -------------------------------------------------------------------------------- /lib/ast_constructors/equationnode.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jKif - 2015 3 | * equationnode.js 4 | * @file AST EquationNode constructor for representing equations 5 | * @author Clark Feusier - cfeusier@gmail.com 6 | * @copyright Copyright (C) Clark Feusier - All Rights Reserved 7 | */ 8 | 9 | 10 | var BaseNode = require('./ast_constructor_base'); 11 | 12 | 13 | /** 14 | * 15 | * @param {Object} locationData 16 | * @param {ASTNode} firstTerm 17 | * @param {ASTNode} secondTerm 18 | * @constructor 19 | */ 20 | function EquationNode(locationData, firstTerm, secondTerm) { 21 | BaseNode.call(this, 'EquationNode', locationData); 22 | this.terms = this.terms || []; 23 | this.terms = this.terms.concat(firstTerm, secondTerm); 24 | } 25 | 26 | 27 | /** 28 | * 29 | * @type {EquationNode} 30 | */ 31 | module.exports = EquationNode; 32 | -------------------------------------------------------------------------------- /lib/ast_constructors/implicationnode.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jKif - 2015 3 | * implicationnode.js 4 | * @file AST ImplicationNode constructor for representing implications 5 | * @author Clark Feusier - cfeusier@gmail.com 6 | * @copyright Copyright (C) Clark Feusier - All Rights Reserved 7 | */ 8 | 9 | 10 | var BaseNode = require('./ast_constructor_base'); 11 | 12 | 13 | /** 14 | * 15 | * @param {Object} locationData 16 | * @param {ASTNode} antecedent 17 | * @param {ASTNode} consequent 18 | * @constructor 19 | */ 20 | function ImplicationNode(locationData, antecedent, consequent) { 21 | BaseNode.call(this, 'ImplicationNode', locationData); 22 | this.antecedent = antecedent; 23 | this.consequent = consequent; 24 | } 25 | 26 | 27 | /** 28 | * 29 | * @type {ImplicationNode} 30 | */ 31 | module.exports = ImplicationNode; 32 | -------------------------------------------------------------------------------- /test/specs/parser/parse/parser_parse_equivalence_test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'), 2 | expect = chai.expect, 3 | Parser = require('../../../../lib/jkif_parser'), 4 | ast = require('../../../../lib/ast_constructors/ast_constructors'); 5 | 6 | 7 | describe('Parser.parse Equivalence sentence parsing', function() { 8 | 9 | it('correctly parses an equivalence into an EquivalenceNode', function () { 10 | var parsed = Parser.parse('(<=> expr1 expr2)').expressions[0]; 11 | expect(parsed).to.be.an.instanceof(ast.EquivalenceNode); 12 | expect(parsed.expressions).to.be.an.instanceof(Array); 13 | expect(parsed.expressions).to.have.length(2); 14 | expect(parsed.expressions[0]).to.be.an.instanceof(ast.WordNode); 15 | expect(parsed.expressions[1]).to.be.an.instanceof(ast.WordNode); 16 | }); 17 | 18 | }); 19 | -------------------------------------------------------------------------------- /lib/ast_constructors/variablenode.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jKif - 2015 3 | * variablenode.js 4 | * @file AST VariableNode constructor for representing independent and row variables 5 | * @author Clark Feusier - cfeusier@gmail.com 6 | * @copyright Copyright (C) Clark Feusier - All Rights Reserved 7 | */ 8 | 9 | 10 | var BaseNode = require('./ast_constructor_base'); 11 | 12 | 13 | /** 14 | * 15 | * @param {Object} locationData 16 | * @param {String} identifier 17 | * @param {String} variableType 18 | * @constructor 19 | */ 20 | function VariableNode(locationData, identifier, variableType) { 21 | BaseNode.call(this, 'VariableNode', locationData); 22 | this.variableType = variableType || 'IND'; 23 | this.variableName = identifier; 24 | } 25 | 26 | 27 | /** 28 | * 29 | * @type {VariableNode} 30 | */ 31 | module.exports = VariableNode; 32 | -------------------------------------------------------------------------------- /lib/ast_constructors/relsentnode.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jKif - 2015 3 | * relsentnode.js 4 | * @file AST RelSentNode constructor for representing implicit relational sentences 5 | * @author Clark Feusier - cfeusier@gmail.com 6 | * @copyright Copyright (C) Clark Feusier - All Rights Reserved 7 | */ 8 | 9 | 10 | var BaseNode = require('./ast_constructor_base'); 11 | 12 | 13 | /** 14 | * 15 | * @param {Object} locationData 16 | * @param {VariableNode|WordNode} constant 17 | * @param {Array} args 18 | * @constructor 19 | */ 20 | function RelSentNode(locationData, constant, args) { 21 | BaseNode.call(this, 'RelSentNode', locationData); 22 | this.constant = constant; 23 | this.argumentList = this.argumentList || []; 24 | this.argumentList = this.argumentList.concat(args); 25 | } 26 | 27 | 28 | /** 29 | * 30 | * @type {RelSentNode} 31 | */ 32 | module.exports = RelSentNode; 33 | -------------------------------------------------------------------------------- /test/specs/parser/parse/parser_parse_universalsent_test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'), 2 | expect = chai.expect, 3 | Parser = require('../../../../lib/jkif_parser'), 4 | ast = require('../../../../lib/ast_constructors/ast_constructors'); 5 | 6 | 7 | describe('Parser.parse Universally-quantified sentence parsing', function() { 8 | 9 | it('correctly parses a universally-quantified sentence into a UniversalSentNode', function () { 10 | var parsed = Parser.parse('(forall (?variable) expr)').expressions[0]; 11 | expect(parsed).to.be.an.instanceof(ast.UniversalSentNode); 12 | expect(parsed.variableList).to.be.an.instanceof(Array); 13 | expect(parsed.variableList).to.have.length(1); 14 | expect(parsed.variableList[0]).to.be.an.instanceof(ast.VariableNode); 15 | expect(parsed.quantifiedSent).to.be.an.instanceof(ast.WordNode); 16 | }); 17 | 18 | }); 19 | -------------------------------------------------------------------------------- /test/specs/parser/parser_interface_test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'), 2 | expect = chai.expect, 3 | Parser = require('../../../lib/jkif_parser'), 4 | ast = require('../../../lib/ast_constructors/ast_constructors'); 5 | 6 | 7 | describe('Parser', function() { 8 | 9 | it('responds to #parse', function() { 10 | expect(Parser).to.respondTo('parse'); 11 | }); 12 | 13 | it('responds to #parseFile', function() { 14 | expect(Parser).to.respondTo('parseFile'); 15 | }); 16 | 17 | it('responds to #parseFileP', function() { 18 | expect(Parser).to.respondTo('parseFileP'); 19 | }); 20 | 21 | it('responds to #writeParsedToFile', function() { 22 | expect(Parser).to.respondTo('writeParsedToFile'); 23 | }); 24 | 25 | it('responds to #writeParsedToFileP', function() { 26 | expect(Parser).to.respondTo('writeParsedToFileP'); 27 | }); 28 | 29 | }); 30 | -------------------------------------------------------------------------------- /lib/ast_constructors/equivalencenode.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jKif - 2015 3 | * equivalencenode.js 4 | * @file AST EquivalenceNode constructor for representing bi-directional implications 5 | * @author Clark Feusier - cfeusier@gmail.com 6 | * @copyright Copyright (C) Clark Feusier - All Rights Reserved 7 | */ 8 | 9 | 10 | var BaseNode = require('./ast_constructor_base'); 11 | 12 | 13 | /** 14 | * 15 | * @param {Object} locationData 16 | * @param {ASTNode} firstExpr 17 | * @param {ASTNode} secondExpr 18 | * @constructor 19 | */ 20 | function EquivalenceNode(locationData, firstExpr, secondExpr) { 21 | BaseNode.call(this, 'EquivalenceNode', locationData); 22 | this.expressions = this.expressions || []; 23 | this.expressions = this.expressions.concat(firstExpr, secondExpr); 24 | } 25 | 26 | 27 | /** 28 | * 29 | * @type {EquivalenceNode} 30 | */ 31 | module.exports = EquivalenceNode; 32 | -------------------------------------------------------------------------------- /test/specs/parser/parse/parser_parse_existentialsent_test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'), 2 | expect = chai.expect, 3 | Parser = require('../../../../lib/jkif_parser'), 4 | ast = require('../../../../lib/ast_constructors/ast_constructors'); 5 | 6 | 7 | describe('Parser.parse Existentially-quantified sentence parsing', function() { 8 | 9 | it('correctly parses an existentially-quantified sentence into an ExistentialSentNode', function () { 10 | var parsed = Parser.parse('(EXISTS (?variable) expr)').expressions[0]; 11 | expect(parsed).to.be.an.instanceof(ast.ExistentialSentNode); 12 | expect(parsed.variableList).to.be.an.instanceof(Array); 13 | expect(parsed.variableList).to.have.length(1); 14 | expect(parsed.variableList[0]).to.be.an.instanceof(ast.VariableNode); 15 | expect(parsed.quantifiedSent).to.be.an.instanceof(ast.WordNode); 16 | }); 17 | 18 | }); 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jkif-parser", 3 | "version": "1.0.0", 4 | "description": "Complete SUO-KIF to JavaScript parser", 5 | "author": "Clark Feusier ", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/jkif/parser" 9 | }, 10 | "bugs": { 11 | "url": "https://github.com/jkif/parser/issues" 12 | }, 13 | "license": "MIT", 14 | "keywords": [ 15 | "jKif", 16 | "KIF", 17 | "SUO-KIF", 18 | "JavaScript", 19 | "Ontology", 20 | "Semantics", 21 | "Logic", 22 | "Parser" 23 | ], 24 | "main": "lib/jkif_parser.js", 25 | "scripts": { 26 | "test": "for f in test/specs/**/*; do echo \"$f\"; mocha \"$f\"; done; exit 0" 27 | }, 28 | "dependencies": { 29 | "bluebird": "^2.9.24", 30 | "jison": "^0.4.15", 31 | "jsonfile": "^2.0.0" 32 | }, 33 | "devDependencies": { 34 | "chai": "^2.2.0", 35 | "mocha": "^2.2.1" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/ast_constructors/universalsentnode.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jKif - 2015 3 | * universalsentnode.js 4 | * @file AST UniversalSentNode constructor for representing universally-quantified logical sentences 5 | * @author Clark Feusier - cfeusier@gmail.com 6 | * @copyright Copyright (C) Clark Feusier - All Rights Reserved 7 | */ 8 | 9 | 10 | var BaseNode = require('./ast_constructor_base'); 11 | 12 | 13 | /** 14 | * 15 | * @param {Object} locationData 16 | * @param {Array} variableList 17 | * @param {ASTNode} quantifiedSent 18 | * @constructor 19 | */ 20 | function UniversalSentNode(locationData, variableList, quantifiedSent) { 21 | BaseNode.call(this, 'UniversalSentNode', locationData); 22 | this.variableList = this.variableList || []; 23 | this.variableList = this.variableList.concat(variableList); 24 | this.quantifiedSent = quantifiedSent; 25 | } 26 | 27 | 28 | /** 29 | * 30 | * @type {UniversalSentNode} 31 | */ 32 | module.exports = UniversalSentNode; 33 | -------------------------------------------------------------------------------- /lib/ast_constructors/existentialsentnode.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jKif - 2015 3 | * existentialsentnode.js 4 | * @file AST ExistentialSentNode constructor for representing existentially-quantified logical sentences 5 | * @author Clark Feusier - cfeusier@gmail.com 6 | * @copyright Copyright (C) Clark Feusier - All Rights Reserved 7 | */ 8 | 9 | 10 | var BaseNode = require('./ast_constructor_base'); 11 | 12 | 13 | /** 14 | * 15 | * @param {Object} locationData 16 | * @param {Array} variableList 17 | * @param {ASTNode} quantifiedSent 18 | * @constructor 19 | */ 20 | function ExistentialSentNode(locationData, variableList, quantifiedSent) { 21 | BaseNode.call(this, 'ExistentialSentNode', locationData); 22 | this.variableList = this.variableList || []; 23 | this.variableList = this.variableList.concat(variableList); 24 | this.quantifiedSent = quantifiedSent; 25 | } 26 | 27 | 28 | /** 29 | * 30 | * @type {ExistentialSentNode} 31 | */ 32 | module.exports = ExistentialSentNode; 33 | -------------------------------------------------------------------------------- /test/specs/ast_constructors/ast_node_constructor_test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'), 2 | expect = chai.expect, 3 | ast = require('../../../lib/ast_constructors/ast_constructors'); 4 | 5 | 6 | describe('ASTnodeConstructor', function() { 7 | 8 | it('should exist', function() { 9 | expect(ast.ASTnodeConstructor).to.exist; 10 | }); 11 | 12 | var locData = { 13 | first_line: 11, 14 | last_line: 12, 15 | first_column: 0, 16 | last_column: 27 17 | }; 18 | 19 | var aNode = new ast.ASTnodeConstructor(null, locData); 20 | 21 | it('should have a "type" property set to "ASTnodeConstructor"', function() { 22 | expect(aNode.type).to.equal('ASTnodeConstructor'); 23 | }); 24 | 25 | it('should have a "locationData" property', function() { 26 | expect(aNode.locationData).to.exist; 27 | }); 28 | 29 | it('should have a "locationData" property that is an instance of Object', function() { 30 | expect(aNode.locationData).to.be.an.instanceof(Object); 31 | }); 32 | 33 | }); 34 | -------------------------------------------------------------------------------- /test/specs/ast_constructors/ast_word_test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'), 2 | expect = chai.expect, 3 | ast = require('../../../lib/ast_constructors/ast_constructors'); 4 | 5 | 6 | describe('WordNode AST Constructor', function() { 7 | 8 | it('should exist', function() { 9 | expect(ast.WordNode).to.exist; 10 | }); 11 | 12 | var locData = { 13 | first_line: 11, 14 | last_line: 12, 15 | first_column: 0, 16 | last_column: 27 17 | }; 18 | 19 | var wNode = new ast.WordNode(locData, 'wordhere'); 20 | 21 | it('should have a "type" property set to "WordNode"', function() { 22 | expect(wNode.type).to.equal('WordNode'); 23 | }); 24 | 25 | it('should have a "locationData" property', function() { 26 | expect(wNode.locationData).to.exist; 27 | }); 28 | 29 | it('should have a "locationData" property that is an instance of Object', function() { 30 | expect(wNode.locationData).to.be.an.instanceof(Object); 31 | }); 32 | 33 | it('should have a "word" property that is a String', function() { 34 | expect(typeof wNode.word).to.equal('string'); 35 | }); 36 | 37 | }); 38 | -------------------------------------------------------------------------------- /test/specs/ast_constructors/ast_kif_test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'), 2 | expect = chai.expect, 3 | ast = require('../../../lib/ast_constructors/ast_constructors'); 4 | 5 | 6 | describe('KIFNode AST Constructor', function() { 7 | 8 | it('should exist', function() { 9 | expect(ast.KIFNode).to.exist; 10 | }); 11 | 12 | var locData = { 13 | first_line: 11, 14 | last_line: 12, 15 | first_column: 0, 16 | last_column: 27 17 | }; 18 | 19 | var kNode = new ast.KIFNode(locData); 20 | 21 | it('should have a "type" property set to "KIFNode"', function() { 22 | expect(kNode.type).to.equal('KIFNode'); 23 | }); 24 | 25 | it('should have a "locationData" property', function() { 26 | expect(kNode.locationData).to.exist; 27 | }); 28 | 29 | it('should have a "locationData" property that is an instance of Object', function() { 30 | expect(kNode.locationData).to.be.an.instanceof(Object); 31 | }); 32 | 33 | it('should have an "expressions" property that is an instance of Array', function() { 34 | expect(kNode.expressions).to.be.an.instanceof(Array); 35 | }); 36 | 37 | }); 38 | -------------------------------------------------------------------------------- /lib/ast_constructors/ast_constructors.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jKif - 2015 3 | * ast_constructors.js 4 | * @file Exports all AST Node constructors on AST object 5 | * @author Clark Feusier - cfeusier@gmail.com 6 | * @copyright Copyright (C) Clark Feusier - All Rights Reserved 7 | */ 8 | 9 | 10 | /** 11 | * 12 | * @type {Object} 13 | */ 14 | module.exports = { 15 | ASTnodeConstructor: require('./ast_constructor_base'), 16 | KIFNode: require('./kifnode'), 17 | WordNode: require('./wordnode'), 18 | VariableNode: require('./variablenode'), 19 | StringLiteralNode: require('./stringliteralnode'), 20 | NumericLiteralNode: require('./numericliteralnode'), 21 | EquationNode: require('./equationnode'), 22 | RelSentNode: require('./relsentnode'), 23 | NegationNode: require('./negationnode'), 24 | DisjunctionNode: require('./disjunctionnode'), 25 | ConjunctionNode: require('./conjunctionnode'), 26 | ImplicationNode: require('./implicationnode'), 27 | EquivalenceNode: require('./equivalencenode'), 28 | UniversalSentNode: require('./universalsentnode'), 29 | ExistentialSentNode: require('./existentialsentnode') 30 | }; 31 | -------------------------------------------------------------------------------- /test/specs/ast_constructors/ast_equation_test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'), 2 | expect = chai.expect, 3 | ast = require('../../../lib/ast_constructors/ast_constructors'); 4 | 5 | 6 | describe('EquationNode AST Constructor', function() { 7 | 8 | it('should exist', function() { 9 | expect(ast.EquationNode).to.exist; 10 | }); 11 | 12 | var locData = { 13 | first_line: 11, 14 | last_line: 12, 15 | first_column: 0, 16 | last_column: 27 17 | }; 18 | 19 | var eNode = new ast.EquationNode(locData); 20 | 21 | it('should have a "type" property set to "EquationNode"', function() { 22 | expect(eNode.type).to.equal('EquationNode'); 23 | }); 24 | 25 | it('should have a "locationData" property', function() { 26 | expect(eNode.locationData).to.exist; 27 | }); 28 | 29 | it('should have a "locationData" property that is an instance of Object', function() { 30 | expect(eNode.locationData).to.be.an.instanceof(Object); 31 | }); 32 | 33 | it('should have a "terms" property that is an instance of Array', function() { 34 | expect(eNode.terms).to.be.an.instanceof(Array); 35 | }); 36 | 37 | }); 38 | -------------------------------------------------------------------------------- /test/specs/ast_constructors/ast_conjunction_test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'), 2 | expect = chai.expect, 3 | ast = require('../../../lib/ast_constructors/ast_constructors'); 4 | 5 | 6 | describe('ConjuctionNode AST Constructor', function() { 7 | 8 | it('should exist', function() { 9 | expect(ast.ConjunctionNode).to.exist; 10 | }); 11 | 12 | var locData = { 13 | first_line: 11, 14 | last_line: 12, 15 | first_column: 0, 16 | last_column: 27 17 | }; 18 | 19 | var cNode = new ast.ConjunctionNode(locData); 20 | 21 | it('should have a "type" property set to "ConjunctionNode"', function() { 22 | expect(cNode.type).to.equal('ConjunctionNode'); 23 | }); 24 | 25 | it('should have a "locationData" property', function() { 26 | expect(cNode.locationData).to.exist; 27 | }); 28 | 29 | it('should have a "locationData" property that is an instance of Object', function() { 30 | expect(cNode.locationData).to.be.an.instanceof(Object); 31 | }); 32 | 33 | it('should have a "conjuncts" property that is an instance of Array', function() { 34 | expect(cNode.conjuncts).to.be.an.instanceof(Array); 35 | }); 36 | 37 | }); 38 | -------------------------------------------------------------------------------- /test/specs/ast_constructors/ast_disjunction_test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'), 2 | expect = chai.expect, 3 | ast = require('../../../lib/ast_constructors/ast_constructors'); 4 | 5 | 6 | describe('DisjunctionNode AST Constructor', function() { 7 | 8 | it('should exist', function() { 9 | expect(ast.DisjunctionNode).to.exist; 10 | }); 11 | 12 | var locData = { 13 | first_line: 11, 14 | last_line: 12, 15 | first_column: 0, 16 | last_column: 27 17 | }; 18 | 19 | var dNode = new ast.DisjunctionNode(locData); 20 | 21 | it('should have a "type" property set to "DisjunctionNode"', function() { 22 | expect(dNode.type).to.equal('DisjunctionNode'); 23 | }); 24 | 25 | it('should have a "locationData" property', function() { 26 | expect(dNode.locationData).to.exist; 27 | }); 28 | 29 | it('should have a "locationData" property that is an instance of Object', function() { 30 | expect(dNode.locationData).to.be.an.instanceof(Object); 31 | }); 32 | 33 | 34 | it('should have a "disjuncts" property that is an instance of Array', function() { 35 | expect(dNode.disjuncts).to.be.an.instanceof(Array); 36 | }); 37 | 38 | }); 39 | -------------------------------------------------------------------------------- /test/specs/ast_constructors/ast_equivalence_test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'), 2 | expect = chai.expect, 3 | ast = require('../../../lib/ast_constructors/ast_constructors'); 4 | 5 | 6 | describe('EquivalenceNode AST Constructor', function() { 7 | 8 | it('should exist', function() { 9 | expect(ast.EquivalenceNode).to.exist; 10 | }); 11 | 12 | var locData = { 13 | first_line: 11, 14 | last_line: 12, 15 | first_column: 0, 16 | last_column: 27 17 | }; 18 | 19 | var eNode = new ast.EquivalenceNode(locData); 20 | 21 | it('should have a "type" property set to "EquivalenceNode"', function() { 22 | expect(eNode.type).to.equal('EquivalenceNode'); 23 | }); 24 | 25 | it('should have a "locationData" property', function() { 26 | expect(eNode.locationData).to.exist; 27 | }); 28 | 29 | it('should have a "locationData" property that is an instance of Object', function() { 30 | expect(eNode.locationData).to.be.an.instanceof(Object); 31 | }); 32 | 33 | it('should have an "expressions" property that is an instance of Array', function() { 34 | expect(eNode.expressions).to.be.an.instanceof(Array); 35 | }); 36 | 37 | }); 38 | -------------------------------------------------------------------------------- /test/specs/ast_constructors/ast_negation_test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'), 2 | expect = chai.expect, 3 | ast = require('../../../lib/ast_constructors/ast_constructors'); 4 | 5 | 6 | describe('NegationNode AST Constructor', function() { 7 | 8 | it('should exist', function () { 9 | expect(ast.NegationNode).to.exist; 10 | }); 11 | 12 | var locData = { 13 | first_line: 11, 14 | last_line: 12, 15 | first_column: 0, 16 | last_column: 27 17 | }; 18 | 19 | var nNode = new ast.NegationNode(locData, new ast.WordNode(locData)); 20 | 21 | it('should have a "type" property set to "NegationNode"', function() { 22 | expect(nNode.type).to.equal('NegationNode'); 23 | }); 24 | 25 | it('should have a "locationData" property', function() { 26 | expect(nNode.locationData).to.exist; 27 | }); 28 | 29 | it('should have a "locationData" property that is an instance of Object', function() { 30 | expect(nNode.locationData).to.be.an.instanceof(Object); 31 | }); 32 | 33 | it('should have a "negatedExpression" property', function() { 34 | expect(nNode.negatedExpression).to.be.an.instanceof(ast.WordNode); 35 | }); 36 | 37 | }); 38 | -------------------------------------------------------------------------------- /test/specs/ast_constructors/ast_numericliteral_test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'), 2 | expect = chai.expect, 3 | ast = require('../../../lib/ast_constructors/ast_constructors'); 4 | 5 | 6 | describe('NumericLiteralNode AST Constructor', function() { 7 | 8 | it('should exist', function() { 9 | expect(ast.NumericLiteralNode).to.exist; 10 | }); 11 | 12 | var locData = { 13 | first_line: 11, 14 | last_line: 12, 15 | first_column: 0, 16 | last_column: 27 17 | }; 18 | 19 | var nLNode = new ast.NumericLiteralNode(locData, 6); 20 | 21 | it('should have a "type" property set to "NumericLiteralNode"', function() { 22 | expect(nLNode.type).to.equal('NumericLiteralNode'); 23 | }); 24 | 25 | it('should have a "locationData" property', function() { 26 | expect(nLNode.locationData).to.exist; 27 | }); 28 | 29 | it('should have a "locationData" property that is an instance of Object', function() { 30 | expect(nLNode.locationData).to.be.an.instanceof(Object); 31 | }); 32 | 33 | it('should have a "number" property that is a Number', function() { 34 | expect(typeof nLNode.number).to.equal('number'); 35 | }); 36 | 37 | }); 38 | -------------------------------------------------------------------------------- /test/specs/parser/parse/parser_parse_comments_test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'), 2 | expect = chai.expect, 3 | Parser = require('../../../../lib/jkif_parser'), 4 | ast = require('../../../../lib/ast_constructors/ast_constructors'); 5 | 6 | 7 | describe('Parser.parse Comments', function() { 8 | 9 | it('correctly parses a single word comment', function() { 10 | expect(Parser.parse(';hi').expressions).to.be.empty; 11 | }); 12 | 13 | it('correctly parses a comment with a space first', function() { 14 | expect(Parser.parse('; hi').expressions).to.be.empty; 15 | }); 16 | 17 | it('correctly parses a single sentence comment', function() { 18 | expect(Parser.parse(';hi my name is comment and i am a comment.').expressions).to.be.empty; 19 | }); 20 | 21 | it('correctly parses a comment and ignores SUO-KIF on the same line', function() { 22 | expect(Parser.parse(';hi my name is comment(?YO argHere)').expressions).to.be.empty; 23 | }); 24 | 25 | it('correctly parses a comment and then SUO-KIF on the line below', function() { 26 | expect(Parser.parse(';hi my name is comment\n(?YO argHere)').expressions).to.not.be.empty; 27 | }); 28 | 29 | }); 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | jKif Parser - Lexical Analysis and Parsing of SUO-KIF into JavaScript Objects 4 | Copyright (C) Clark Feusier - All Rights Reserved 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /test/specs/ast_constructors/ast_stringliteral_test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'), 2 | expect = chai.expect, 3 | ast = require('../../../lib/ast_constructors/ast_constructors'); 4 | 5 | 6 | describe('StringLiteralNode AST Constructor', function() { 7 | 8 | it('should exist', function() { 9 | expect(ast.StringLiteralNode).to.exist; 10 | }); 11 | 12 | var locData = { 13 | first_line: 11, 14 | last_line: 12, 15 | first_column: 0, 16 | last_column: 27 17 | }; 18 | 19 | var sLNode = new ast.StringLiteralNode(locData, "blahasldjf1sdljkfBOOM"); 20 | 21 | it('should have a "type" property set to "StringLiteralNode"', function() { 22 | expect(sLNode.type).to.equal('StringLiteralNode'); 23 | }); 24 | 25 | it('should have a "locationData" property', function() { 26 | expect(sLNode.locationData).to.exist; 27 | }); 28 | 29 | it('should have a "locationData" property that is an instance of Object', function() { 30 | expect(sLNode.locationData).to.be.an.instanceof(Object); 31 | }); 32 | 33 | it('should have a "rawString" property', function() { 34 | expect(typeof sLNode.rawString).to.equal('string'); 35 | }); 36 | 37 | it('should have a "chars" property', function() { 38 | expect(typeof sLNode.chars).to.equal('string'); 39 | }); 40 | 41 | }); 42 | -------------------------------------------------------------------------------- /test/specs/ast_constructors/ast_variable_test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'), 2 | expect = chai.expect, 3 | ast = require('../../../lib/ast_constructors/ast_constructors'); 4 | 5 | 6 | describe('VariableNode AST Constructor', function() { 7 | 8 | it('should exist', function() { 9 | expect(ast.VariableNode).to.exist; 10 | }); 11 | 12 | var locData = { 13 | first_line: 11, 14 | last_line: 12, 15 | first_column: 0, 16 | last_column: 27 17 | }; 18 | 19 | var vNode = new ast.VariableNode(locData, 'VARIABLENAME'); 20 | 21 | it('should have a "type" property set to "VariableNode"', function() { 22 | expect(vNode.type).to.equal('VariableNode'); 23 | }); 24 | 25 | it('should have a "locationData" property', function() { 26 | expect(vNode.locationData).to.exist; 27 | }); 28 | 29 | it('should have a "locationData" property that is an instance of Object', function() { 30 | expect(vNode.locationData).to.be.an.instanceof(Object); 31 | }); 32 | 33 | it('should have a "variableType" property that is a String', function() { 34 | expect(typeof vNode.variableType).to.equal('string'); 35 | }); 36 | 37 | it('should have a "variableName" property that is a String', function() { 38 | expect(typeof vNode.variableName).to.equal('string'); 39 | }); 40 | 41 | }); 42 | -------------------------------------------------------------------------------- /test/specs/ast_constructors/ast_implication_test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'), 2 | expect = chai.expect, 3 | ast = require('../../../lib/ast_constructors/ast_constructors'); 4 | 5 | 6 | describe('ImplicationNode AST Constructor', function() { 7 | 8 | it('should exist', function() { 9 | expect(ast.ImplicationNode).to.exist; 10 | }); 11 | 12 | var locData = { 13 | first_line: 11, 14 | last_line: 12, 15 | first_column: 0, 16 | last_column: 27 17 | }; 18 | 19 | var iNode = new ast.ImplicationNode(locData, new ast.WordNode(locData), new ast.WordNode(locData)); 20 | 21 | it('should have a "type" property set to "ImplicationNode"', function() { 22 | expect(iNode.type).to.equal('ImplicationNode'); 23 | }); 24 | 25 | it('should have a "locationData" property', function() { 26 | expect(iNode.locationData).to.exist; 27 | }); 28 | 29 | it('should have a "locationData" property that is an instance of Object', function() { 30 | expect(iNode.locationData).to.be.an.instanceof(Object); 31 | }); 32 | 33 | it('should have an "antecedent" property', function() { 34 | expect(iNode.antecedent).to.be.an.instanceof(ast.WordNode); 35 | }); 36 | 37 | it('should have a "consequent" property', function() { 38 | expect(iNode.consequent).to.be.an.instanceof(ast.WordNode); 39 | }); 40 | 41 | }); 42 | -------------------------------------------------------------------------------- /test/specs/ast_constructors/ast_relsent_test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'), 2 | expect = chai.expect, 3 | ast = require('../../../lib/ast_constructors/ast_constructors'); 4 | 5 | 6 | describe('RelSentNode AST Constructor', function() { 7 | 8 | it('should exist', function() { 9 | expect(ast.RelSentNode).to.exist; 10 | }); 11 | 12 | var locData = { 13 | first_line: 11, 14 | last_line: 12, 15 | first_column: 0, 16 | last_column: 27 17 | }; 18 | 19 | var rSNode = new ast.RelSentNode(locData, new ast.VariableNode(locData)); 20 | 21 | it('should have a "type" property set to "RelSentNode"', function() { 22 | expect(rSNode.type).to.equal('RelSentNode'); 23 | }); 24 | 25 | it('should have a "locationData" property', function() { 26 | expect(rSNode.locationData).to.exist; 27 | }); 28 | 29 | it('should have a "locationData" property that is an instance of Object', function() { 30 | expect(rSNode.locationData).to.be.an.instanceof(Object); 31 | }); 32 | 33 | it('should have a "constant" property that is a VariableNode', function() { 34 | expect(rSNode.constant).to.be.an.instanceof(ast.VariableNode); 35 | }); 36 | 37 | it('should have an "argumentList" property that is an instance of Array', function() { 38 | expect(rSNode.argumentList).to.be.an.instanceof(Array); 39 | }); 40 | 41 | }); 42 | -------------------------------------------------------------------------------- /test/specs/parser/parse/parser_parse_variable_test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'), 2 | expect = chai.expect, 3 | Parser = require('../../../../lib/jkif_parser'), 4 | ast = require('../../../../lib/ast_constructors/ast_constructors'); 5 | 6 | 7 | describe('Parser.parse Variable parsing', function() { 8 | 9 | it('correctly parses Row Variables into VariableNodes', function () { 10 | expect(Parser.parse('@row').expressions[0]).to.be.an.instanceof(ast.VariableNode); 11 | }); 12 | 13 | it('correctly parses Independent Variables into VariableNodes', function () { 14 | expect(Parser.parse('?ind').expressions[0]).to.be.an.instanceof(ast.VariableNode); 15 | }); 16 | 17 | it('correctly parses Variables with variableType "ROW"', function () { 18 | expect(Parser.parse('@row').expressions[0].variableType).to.equal('ROW'); 19 | }); 20 | 21 | it('correctly parses Variables with variableType "IND"', function () { 22 | expect(Parser.parse('?ind').expressions[0].variableType).to.equal('IND'); 23 | }); 24 | 25 | it('correctly parses Row Variable names', function () { 26 | expect(Parser.parse('@row').expressions[0].variableName).to.equal('row'); 27 | }); 28 | 29 | it('correctly parses Independent Variable names', function () { 30 | expect(Parser.parse('?ind').expressions[0].variableName).to.equal('ind'); 31 | }); 32 | 33 | }); 34 | -------------------------------------------------------------------------------- /test/specs/ast_constructors/ast_universalsent_test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'), 2 | expect = chai.expect, 3 | ast = require('../../../lib/ast_constructors/ast_constructors'); 4 | 5 | 6 | describe('UniversalSentNode AST Constructor', function() { 7 | 8 | it('should exist', function() { 9 | expect(ast.UniversalSentNode).to.exist; 10 | }); 11 | 12 | var locData = { 13 | first_line: 11, 14 | last_line: 12, 15 | first_column: 0, 16 | last_column: 27 17 | }; 18 | 19 | var uSNode = new ast.UniversalSentNode(locData, [], new ast.WordNode(locData)); 20 | 21 | it('should have a "type" property set to "UniversalSentNode"', function() { 22 | expect(uSNode.type).to.equal('UniversalSentNode'); 23 | }); 24 | 25 | it('should have a "locationData" property', function() { 26 | expect(uSNode.locationData).to.exist; 27 | }); 28 | 29 | it('should have a "locationData" property that is an instance of Object', function() { 30 | expect(uSNode.locationData).to.be.an.instanceof(Object); 31 | }); 32 | 33 | it('should have a "quantifiedSent" property', function() { 34 | expect(uSNode.quantifiedSent).to.be.an.instanceof(ast.WordNode); 35 | }); 36 | 37 | it('should have a "variableList" property that is an instance of Array', function() { 38 | expect(uSNode.variableList).to.be.an.instanceof(Array); 39 | }); 40 | 41 | }); 42 | -------------------------------------------------------------------------------- /test/specs/ast_constructors/ast_existentialsent_test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'), 2 | expect = chai.expect, 3 | ast = require('../../../lib/ast_constructors/ast_constructors'); 4 | 5 | 6 | describe('ExistentialSentNode AST Constructor', function() { 7 | 8 | it('should exist', function() { 9 | expect(ast.ExistentialSentNode).to.exist; 10 | }); 11 | 12 | var locData = { 13 | first_line: 11, 14 | last_line: 12, 15 | first_column: 0, 16 | last_column: 27 17 | }; 18 | 19 | var eNode = new ast.ExistentialSentNode(locData, [], new ast.WordNode(locData)); 20 | 21 | it('should have a "type" property set to "ExistentialSentNode"', function() { 22 | expect(eNode.type).to.equal('ExistentialSentNode'); 23 | }); 24 | 25 | it('should have a "locationData" property', function() { 26 | expect(eNode.locationData).to.exist; 27 | }); 28 | 29 | it('should have a "locationData" property that is an instance of Object', function() { 30 | expect(eNode.locationData).to.be.an.instanceof(Object); 31 | }); 32 | 33 | it('should have a "variableList" property that is an instance of Array', function() { 34 | expect(eNode.variableList).to.be.an.instanceof(Array); 35 | }); 36 | 37 | it('should have a "quantifiedSent" property that is an instance of Array', function() { 38 | expect(eNode.quantifiedSent).to.be.an.instanceof(ast.WordNode); 39 | }); 40 | 41 | }); 42 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to jKif Parser 2 | 3 | ## General Workflow 4 | 5 | 1. Fork the repo 6 | 1. Cut namespaced feature branch with initials from master 7 | - FL_bug/... 8 | - FL_feat/... 9 | - FL_test/... 10 | - FL_doc/... 11 | - FL_refactor/... 12 | 1. Make commits to your feature branch (only make changes that are relevant to this branch) 13 | - commit messages should start with a capital letter 14 | - commit messages should be in the present tense 15 | - commit messages should not end with a '.' 16 | 1. When you've finished with your fix or feature: 17 | - `git fetch upstream master` 18 | - `git rebase upstream/master` 19 | - submit a pull request directly to master. Include a description of your changes. 20 | 1. Your pull request will be reviewed by another maintainer. The point of code reviews is to help keep the codebase clean and of high quality. 21 | 1. Fix any issues raised by your code reviewer, and push your fixes as a single new commit. 22 | 1. Once the pull request has been reviewed, it will be merged by another member of the team. Do not merge your own pull requests. 23 | 24 | ### Guidelines 25 | 26 | 1. Uphold the current code standard: 27 | - Keep your code DRY. 28 | - Follow [STYLE_GUIDE.md](STYLE_GUIDE.md) 29 | 1. Run the tests before submitting a pull request. 30 | 1. Submit tests if your pull request contains new, testable behavior. 31 | 32 | 33 | **Thanks for contributing!** -------------------------------------------------------------------------------- /test/specs/parser/parse/parser_parse_stringliteral_test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'), 2 | expect = chai.expect, 3 | Parser = require('../../../../lib/jkif_parser'), 4 | ast = require('../../../../lib/ast_constructors/ast_constructors'); 5 | 6 | 7 | describe('Parser.parse StringLiteral parsing', function() { 8 | 9 | it('correctly parses a StringLiteral into a StringLiteralNode', function() { 10 | expect(Parser.parse('""').expressions[0]).to.be.instanceof(ast.StringLiteralNode); 11 | expect(Parser.parse('\'\'').expressions[0]).to.be.instanceof(ast.StringLiteralNode); 12 | }); 13 | 14 | it('correctly parses a StringLiteral with special characters', function() { 15 | expect(Parser.parse('"!$_-@~^#%+=\\"').expressions[0]).to.be.instanceof(ast.StringLiteralNode); 16 | }); 17 | 18 | it('correctly parses a StringLiteral with new lines', function() { 19 | expect(Parser.parse('"\n"').expressions[0]).to.be.instanceof(ast.StringLiteralNode); 20 | }); 21 | 22 | it('correctly parses a StringLiteral with new lines', function() { 23 | expect(Parser.parse('"\n"').expressions[0]).to.be.instanceof(ast.StringLiteralNode); 24 | }); 25 | 26 | it('correctly parses a StringLiteral into a raw string', function() { 27 | expect(Parser.parse('"abcdefg hi"').expressions[0].rawString).to.equal('"abcdefg hi"'); 28 | }); 29 | 30 | it('correctly parses a StringLiteral characters out of the quotation marks', function() { 31 | expect(Parser.parse('"abcdefg hi"').expressions[0].chars).to.equal('abcdefg hi'); 32 | }); 33 | 34 | }); 35 | -------------------------------------------------------------------------------- /lib/jkif_parser.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jKif Parser - 2015 3 | * jkif_parser.js 4 | * @file Lexical and Syntactic Analysis of SUO-KIF via JavaScript 5 | * @author Clark Feusier - cfeusier@gmail.com 6 | * @copyright Copyright (C) Clark Feusier - All Rights Reserved 7 | */ 8 | 9 | 10 | var fs = require('fs'), 11 | fsJson = require('jsonfile'), 12 | p = require('bluebird'), 13 | jParser = require('jison').Parser, 14 | grammars = fs.readFileSync(__dirname + '/grammars.jison', 'utf8'), 15 | Parser = new jParser(grammars); 16 | 17 | 18 | /** 19 | * 20 | * @public 21 | * @param {String} filePath 22 | * @param {Function} cb 23 | */ 24 | Parser.parseFile = function(filePath, cb) { 25 | fs.readFile(filePath, 'utf8', function(err, kif) { 26 | err ? cb(err) : cb(null, Parser.parse(kif)); 27 | }); 28 | }; 29 | 30 | 31 | /** 32 | * 33 | * @public 34 | * @param {String} filePath 35 | * @param {KIFNode} parsed 36 | * @param {Function} cb 37 | */ 38 | Parser.writeParsedToFile = function(filePath, parsed, cb) { 39 | fsJson.writeFile(filePath, parsed, function(err) { 40 | err ? cb(err) : cb(null); 41 | }); 42 | }; 43 | 44 | 45 | /** 46 | * 47 | * @public 48 | * @param {String} filePath 49 | */ 50 | Parser.parseFileP = p.promisify(Parser.parseFile); 51 | 52 | 53 | /** 54 | * 55 | * @public 56 | * @param {String} filePath 57 | * @param {KIFNode} parsed 58 | */ 59 | Parser.writeParsedToFileP = p.promisify(Parser.writeParsedToFile); 60 | 61 | 62 | /** 63 | * 64 | * @public 65 | * @type {Parser} 66 | */ 67 | module.exports = Parser; 68 | -------------------------------------------------------------------------------- /test/specs/parser/extras/parser_extras_cb_test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'), 2 | expect = chai.expect, 3 | fs = require('fs'), 4 | fsJson = require('jsonfile'), 5 | path = require('path'), 6 | Parser = require('../../../../lib/jkif_parser'), 7 | ast = require('../../../../lib/ast_constructors/ast_constructors'), 8 | testResourcesPath = path.resolve(__dirname + '../../../../resources/'), 9 | kifFilePath = path.resolve(testResourcesPath + '/sumo_core.kif'), 10 | jsonTestOutputPath = path.resolve(testResourcesPath + '/test_output.json'); 11 | 12 | 13 | describe('Parser Extras Callback-Style', function() { 14 | 15 | describe('#parseFile', function() { 16 | it('should parse a .kif file into a KIFNode', function(done) { 17 | Parser.parseFile(kifFilePath, function(err, parsed) { 18 | expect(parsed).to.be.an.instanceof(ast.KIFNode); 19 | done(); 20 | }); 21 | }); 22 | }); 23 | 24 | describe('#writeParsedToFile', function() { 25 | var parsed; 26 | beforeEach(function(done) { 27 | Parser.parseFile(kifFilePath, function(err, kif) { 28 | parsed = kif; 29 | Parser.writeParsedToFile(jsonTestOutputPath, parsed, function(success) { 30 | done(); 31 | }); 32 | }); 33 | }); 34 | 35 | afterEach(function(done) { 36 | fs.unlink(jsonTestOutputPath, function() { 37 | done(); 38 | }); 39 | }); 40 | 41 | it('should write parsed kif to a file', function(done) { 42 | fsJson.readFile(jsonTestOutputPath, function(err, data) { 43 | expect(data.type).to.equal('KIFNode'); 44 | done(); 45 | }); 46 | }); 47 | }); 48 | 49 | }); 50 | -------------------------------------------------------------------------------- /test/specs/parser/parse/parser_parse_sumo_test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'), 2 | expect = chai.expect, 3 | fs = require('fs'), 4 | path = require('path'), 5 | Parser = require('../../../../lib/jkif_parser'), 6 | ast = require('../../../../lib/ast_constructors/ast_constructors'), 7 | kifFile = fs.readFileSync(path.resolve(__dirname + './../../../resources/sumo_core.kif'), 'utf8'), 8 | parsed = Parser.parse(kifFile); 9 | 10 | 11 | describe('Parser SUMO KIF File Parser', function() { 12 | 13 | it('should parse a SUMO .kif file into a KIFNode', function() { 14 | expect(parsed).to.be.an.instanceof(ast.KIFNode); 15 | }); 16 | 17 | it('should parse complex sentences correctly', function() { 18 | var exprs = parsed.expressions; 19 | var existSent = exprs[0]; 20 | expect(existSent).to.be.an.instanceof(ast.ExistentialSentNode); 21 | expect(existSent.quantifiedSent).to.be.an.instanceof(ast.ConjunctionNode); 22 | expect(existSent.quantifiedSent.conjuncts[0]).to.be.an.instanceof(ast.RelSentNode); 23 | }); 24 | 25 | it('should parse complex definitions correctly', function() { 26 | var exprs = parsed.expressions; 27 | var equivSent = exprs[6]; 28 | var existNested = equivSent.expressions[1]; 29 | var conjunctSent = existNested.quantifiedSent; 30 | var implSent = conjunctSent.conjuncts[2]; 31 | expect(equivSent).to.be.an.instanceof(ast.EquivalenceNode); 32 | expect(existNested).to.be.an.instanceof(ast.ExistentialSentNode); 33 | expect(conjunctSent).to.be.an.instanceof(ast.ConjunctionNode); 34 | expect(implSent).to.be.an.instanceof(ast.ImplicationNode); 35 | expect(implSent.consequent.quantifiedSent).to.be.an.instanceof(ast.RelSentNode); 36 | }); 37 | 38 | }); 39 | -------------------------------------------------------------------------------- /test/specs/parser/parse/parser_parse_numericliteral_test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'), 2 | expect = chai.expect, 3 | Parser = require('../../../../lib/jkif_parser'), 4 | ast = require('../../../../lib/ast_constructors/ast_constructors'); 5 | 6 | 7 | describe('Parser.parse NumericLiteral parsing', function() { 8 | 9 | it('correctly parses a NumericLiteral positive integer into a NumericLiteralNode', function() { 10 | expect(Parser.parse('1').expressions[0]).to.be.instanceof(ast.NumericLiteralNode); 11 | }); 12 | 13 | it('correctly parses a NumericLiteral positive decimal into a NumericLiteralNode', function() { 14 | expect(Parser.parse('0.1123').expressions[0]).to.be.instanceof(ast.NumericLiteralNode); 15 | }); 16 | 17 | it('correctly parses a NumericLiteral negative integer into a NumericLiteralNode', function() { 18 | expect(Parser.parse('-6').expressions[0]).to.be.instanceof(ast.NumericLiteralNode); 19 | }); 20 | 21 | it('correctly parses a NumericLiteral negative decimal into a NumericLiteralNode', function() { 22 | expect(Parser.parse('-6.999').expressions[0]).to.be.instanceof(ast.NumericLiteralNode); 23 | }); 24 | 25 | it('correctly parses the number value out of a NumericLiteral for positive integers', function() { 26 | expect(Parser.parse('6').expressions[0].number).to.equal(6); 27 | }); 28 | 29 | it('correctly parses the number value out of a NumericLiteral for negative integers', function() { 30 | expect(Parser.parse('-6').expressions[0].number).to.equal(-6); 31 | }); 32 | 33 | it('correctly parses the number value out of a NumericLiteral for positive decimals', function() { 34 | expect(Parser.parse('0.166').expressions[0].number).to.equal(0.166); 35 | }); 36 | 37 | it('correctly parses the number value out of a NumericLiteral for negative decimals', function() { 38 | expect(Parser.parse('-0.166').expressions[0].number).to.equal(-0.166); 39 | }); 40 | 41 | }); 42 | -------------------------------------------------------------------------------- /test/specs/parser/extras/parser_extras_p_test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'), 2 | expect = chai.expect, 3 | fs = require('fs'), 4 | fsJson = require('jsonfile'), 5 | path = require('path'), 6 | Parser = require('../../../../lib/jkif_parser'), 7 | ast = require('../../../../lib/ast_constructors/ast_constructors'), 8 | testResourcesPath = path.resolve(__dirname + '../../../../resources/'), 9 | kifFilePath = path.resolve(testResourcesPath + '/sumo_core.kif'), 10 | jsonTestOutputPath = path.resolve(testResourcesPath + '/test_output.json'); 11 | 12 | 13 | describe('Parser Extras Promise-Style', function() { 14 | 15 | describe('#parseFileP', function() { 16 | it('should parse a .kif file into a KIFNode', function(done) { 17 | Parser.parseFileP(kifFilePath).then(function(parsed) { 18 | expect(parsed).to.be.an.instanceof(ast.KIFNode); 19 | done(); 20 | }).catch(function(err) { 21 | console.error(err); 22 | done(); 23 | }); 24 | }); 25 | }); 26 | 27 | describe('#writeParsedToFileP', function() { 28 | var parsed; 29 | beforeEach(function(done) { 30 | Parser.parseFileP(kifFilePath).then(function(kif) { 31 | parsed = kif; 32 | Parser.writeParsedToFileP(jsonTestOutputPath, parsed).then(function(success) { 33 | done(); 34 | }).catch(function(err) { 35 | console.error(err); 36 | done(); 37 | }); 38 | }).catch(function(err) { 39 | console.error(err); 40 | done(); 41 | }); 42 | }); 43 | 44 | afterEach(function(done) { 45 | fs.unlink(jsonTestOutputPath, function() { 46 | done(); 47 | }); 48 | }); 49 | 50 | it('should write parsed kif to a file', function(done) { 51 | fsJson.readFile(jsonTestOutputPath, function(err, data) { 52 | expect(data.type).to.equal('KIFNode'); 53 | done(); 54 | }); 55 | }); 56 | }); 57 | 58 | }); 59 | -------------------------------------------------------------------------------- /test/resources/sumo_core.kif: -------------------------------------------------------------------------------- 1 | ;; A farmer likes a tractor ;) 2 | (exists (?F ?T) 3 | (and 4 | (instance ?F Farmer) 5 | (instance ?T Tractor) 6 | (likes ?F ?T))) 7 | 8 | ;; This is a core definition of the SUMO 'Class' 9 | (<=> 10 | (instance ?CLASS Class) 11 | (subclass ?CLASS Entity)) ; Entity is the core object in SUMO 12 | 13 | (exists (?THING) 14 | (instance ?THING Entity)) 15 | (forall (?THING) 16 | (instance ?THING Entity)) 17 | 18 | (=> 19 | (disjointDecomposition @ROW) 20 | (=> 21 | (inList ?ELEMENT 22 | (ListFn @ROW)) 23 | (instance ?ELEMENT Class))) 24 | 25 | (=> 26 | (exhaustiveDecomposition @ROW) 27 | (=> 28 | (inList ?ELEMENT 29 | (ListFn @ROW)) 30 | (instance ?ELEMENT Class))) 31 | 32 | (<=> 33 | (instance ?REL TotalValuedRelation) 34 | (exists (?VALENCE) 35 | (and 36 | (instance ?REL Relation) 37 | (valence ?REL ?VALENCE) 38 | (=> 39 | (forall (?NUMBER ?ELEMENT ?CLASS) 40 | (=> 41 | (and 42 | (lessThan ?NUMBER ?VALENCE) 43 | (domain ?REL ?NUMBER ?CLASS) 44 | (equal ?ELEMENT 45 | (ListOrderFn 46 | (ListFn @ROW) ?NUMBER))) 47 | (instance ?ELEMENT ?CLASS))) 48 | (exists (?ITEM) 49 | (?REL @ROW ?ITEM)))))) 50 | 51 | (<=> 52 | (instance ?ABS Abstract) 53 | (not 54 | (exists (?POINT) 55 | (or 56 | (located ?ABS ?POINT) 57 | (time ?ABS ?POINT))))) 58 | 59 | (=> 60 | (and 61 | (playsRoleInEvent ?OBJ ?ROLE ?EVENT) 62 | (instance ?EVENT ?TYPE) 63 | (subclass ?TYPE Process) 64 | (time ?EVENT ?TIME) 65 | (eventLocated ?EVENT ?PLACE)) 66 | (playsRoleInEventOfType ?OBJ ?ROLE ?TYPE ?TIME ?PLACE)) 67 | 68 | (=> 69 | (and 70 | (playsRoleInEvent ?OBJ ?ROLE ?EVENT) 71 | (instance ?EVENT ?CLASS) 72 | (subclass ?CLASS Process) 73 | (time ?EVENT ?TIME) 74 | (eventLocated ?EVENT ?PLACE)) 75 | (playsRoleInEventOfType ?OBJ ?ROLE ?CLASS ?TIME ?PLACE)) -------------------------------------------------------------------------------- /test/specs/parser/parse/parser_parse_exponent_test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'), 2 | expect = chai.expect, 3 | Parser = require('../../../../lib/jkif_parser'), 4 | ast = require('../../../../lib/ast_constructors/ast_constructors'); 5 | 6 | 7 | describe('Parser.parse Exponent parsing', function() { 8 | 9 | it('correctly parses a NumericLiteral positive integer exponent into a NumericLiteralNode', function() { 10 | expect(Parser.parse('1e8').expressions[0]).to.be.instanceof(ast.NumericLiteralNode); 11 | }); 12 | 13 | it('correctly parses a NumericLiteral positive decimal exponent into a NumericLiteralNode', function() { 14 | expect(Parser.parse('0.112E12').expressions[0]).to.be.instanceof(ast.NumericLiteralNode); 15 | }); 16 | 17 | it('correctly parses a NumericLiteral negative integer exponent into a NumericLiteralNode', function() { 18 | expect(Parser.parse('-6e9').expressions[0]).to.be.instanceof(ast.NumericLiteralNode); 19 | }); 20 | 21 | it('correctly parses a NumericLiteral negative decimal exponent into a NumericLiteralNode', function() { 22 | expect(Parser.parse('-6.999e3').expressions[0]).to.be.instanceof(ast.NumericLiteralNode); 23 | }); 24 | 25 | it('correctly parses the number value out of a NumericLiteral for positive integer exponents', function() { 26 | expect(Parser.parse('6E3').expressions[0].number).to.equal(6000); 27 | }); 28 | 29 | it('correctly parses the number value out of a NumericLiteral for negative integer exponents', function() { 30 | expect(Parser.parse('-6e6').expressions[0].number).to.equal(-6000000); 31 | }); 32 | 33 | it('correctly parses the number value out of a NumericLiteral for positive decimal exponents', function() { 34 | expect(Parser.parse('0.166E4').expressions[0].number).to.equal(1660); 35 | }); 36 | 37 | it('correctly parses the number value out of a NumericLiteral for negative decimal exponents', function() { 38 | expect(Parser.parse('-4.3e1').expressions[0].number).to.equal(-43); 39 | }); 40 | 41 | }); 42 | -------------------------------------------------------------------------------- /test/specs/parser/parse/parser_parse_negation_test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'), 2 | expect = chai.expect, 3 | Parser = require('../../../../lib/jkif_parser'), 4 | ast = require('../../../../lib/ast_constructors/ast_constructors'); 5 | 6 | 7 | describe('Parser.parse Negated sentence parsing', function() { 8 | 9 | it('correctly parses a negation into a NegationNode', function() { 10 | var parsed = Parser.parse('(not word)').expressions[0]; 11 | expect(parsed).to.be.an.instanceof(ast.NegationNode); 12 | }); 13 | 14 | it('correctly parses a negated word', function() { 15 | var parsed = Parser.parse('(not word)').expressions[0]; 16 | expect(parsed.negatedExpression).to.be.an.instanceof(ast.WordNode); 17 | }); 18 | 19 | it('correctly parses a negated variable', function() { 20 | var parsed = Parser.parse('(not ?VARIABLE)').expressions[0]; 21 | expect(parsed.negatedExpression).to.be.an.instanceof(ast.VariableNode); 22 | }); 23 | 24 | it('correctly parses a negated string literal', function() { 25 | var parsed = Parser.parse('(not "stringhere")').expressions[0]; 26 | expect(parsed.negatedExpression).to.be.an.instanceof(ast.StringLiteralNode); 27 | }); 28 | 29 | it('correctly parses a negated numeric literal', function() { 30 | var parsed = Parser.parse('(not 6)').expressions[0]; 31 | expect(parsed.negatedExpression).to.be.an.instanceof(ast.NumericLiteralNode); 32 | }); 33 | 34 | it('correctly parses a negated equation', function() { 35 | var parsed = Parser.parse('(not (= firstTerm secondTerm))').expressions[0]; 36 | expect(parsed.negatedExpression).to.be.an.instanceof(ast.EquationNode); 37 | }); 38 | 39 | it('correctly parses a negated RelSent', function() { 40 | var parsed = Parser.parse('(not (?VARIABLE argument))').expressions[0]; 41 | expect(parsed.negatedExpression).to.be.an.instanceof(ast.RelSentNode); 42 | }); 43 | 44 | it('correctly parses a negated FunctionTerm', function() { 45 | var parsed = Parser.parse('(not (function argument))').expressions[0]; 46 | expect(parsed.negatedExpression).to.be.an.instanceof(ast.RelSentNode); 47 | }); 48 | 49 | }); 50 | -------------------------------------------------------------------------------- /test/specs/ast_constructors/ast_manifest_test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'), 2 | expect = chai.expect, 3 | ast = require('../../../lib/ast_constructors/ast_constructors'); 4 | 5 | 6 | describe('AST Constructors Manifest Export', function() { 7 | 8 | it('should exist', function() { 9 | expect(ast).to.exist; 10 | }); 11 | 12 | it('should export an Object', function() { 13 | expect(ast).to.be.an.instanceof(Object); 14 | }); 15 | 16 | it('should have an ASTnodeConstructor as a property', function() { 17 | expect(ast.ASTnodeConstructor).to.exist; 18 | }); 19 | 20 | it('should have a KIFNode Constructor as a property', function() { 21 | expect(ast.KIFNode).to.exist; 22 | }); 23 | 24 | it('should have a WordNode Constructor as a property', function() { 25 | expect(ast.WordNode).to.exist; 26 | }); 27 | 28 | it('should have a VariableNode Constructor as a property', function() { 29 | expect(ast.VariableNode).to.exist; 30 | }); 31 | 32 | it('should have a StringLiteralNode Constructor as a property', function() { 33 | expect(ast.StringLiteralNode).to.exist; 34 | }); 35 | 36 | it('should have a NumericLiteralNode Constructor as a property', function() { 37 | expect(ast.NumericLiteralNode).to.exist; 38 | }); 39 | 40 | it('should have an EquationNode Constructor as a property', function() { 41 | expect(ast.EquationNode).to.exist; 42 | }); 43 | 44 | it('should have a RelSentNode Constructor as a property', function() { 45 | expect(ast.RelSentNode).to.exist; 46 | }); 47 | 48 | it('should have a NegationNode Constructor as a property', function() { 49 | expect(ast.NegationNode).to.exist; 50 | }); 51 | 52 | it('should have a DisjunctionNode Constructor as a property', function() { 53 | expect(ast.DisjunctionNode).to.exist; 54 | }); 55 | 56 | it('should have a ConjunctionNode Constructor as a property', function() { 57 | expect(ast.ConjunctionNode).to.exist; 58 | }); 59 | 60 | it('should have an ImplicationNode Constructor as a property', function() { 61 | expect(ast.ImplicationNode).to.exist; 62 | }); 63 | 64 | it('should have an EquivalenceNode Constructor as a property', function() { 65 | expect(ast.EquivalenceNode).to.exist; 66 | }); 67 | 68 | it('should have a UniversalSentNode Constructor as a property', function() { 69 | expect(ast.UniversalSentNode).to.exist; 70 | }); 71 | 72 | it('should have an ExistentialSentNode Constructor as a property', function() { 73 | expect(ast.ExistentialSentNode).to.exist; 74 | }); 75 | 76 | }); 77 | -------------------------------------------------------------------------------- /test/specs/parser/parse/parser_parse_equation_test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'), 2 | expect = chai.expect, 3 | Parser = require('../../../../lib/jkif_parser'), 4 | ast = require('../../../../lib/ast_constructors/ast_constructors'); 5 | 6 | 7 | describe('Parser.parse Equation parsing', function() { 8 | 9 | it('correctly parses an Equation into an EquationNode', function() { 10 | var parsed = Parser.parse('(= id id)').expressions[0]; 11 | expect(parsed).to.be.an.instanceof(ast.EquationNode); 12 | }); 13 | 14 | it('correctly parses the terms of an Equation if both Words', function() { 15 | var parsed = Parser.parse('(= firstTerm secondTerm)').expressions[0]; 16 | expect(parsed.terms).to.have.length(2); 17 | expect(parsed.terms[0]).to.be.an.instanceof(ast.WordNode); 18 | expect(parsed.terms[1]).to.be.an.instanceof(ast.WordNode); 19 | expect(parsed.terms[0].word).to.equal('firstTerm'); 20 | expect(parsed.terms[1].word).to.equal('secondTerm'); 21 | }); 22 | 23 | it('correctly parses the terms of an Equation if both Variables', function() { 24 | var parsed = Parser.parse('(= ?FIRSTTERM ?SECONDTERM)').expressions[0]; 25 | expect(parsed.terms).to.have.length(2); 26 | expect(parsed.terms[0]).to.be.an.instanceof(ast.VariableNode); 27 | expect(parsed.terms[1]).to.be.an.instanceof(ast.VariableNode); 28 | expect(parsed.terms[0].variableName).to.equal('FIRSTTERM'); 29 | expect(parsed.terms[1].variableName).to.equal('SECONDTERM'); 30 | expect(parsed.terms[0].variableType).to.equal('IND'); 31 | expect(parsed.terms[1].variableType).to.equal('IND'); 32 | }); 33 | 34 | it('correctly parses the terms of an Equation with a FunctionTerm', function() { 35 | var parsed = Parser.parse('(= firstTerm (function argument))').expressions[0]; 36 | expect(parsed.terms).to.have.length(2); 37 | expect(parsed.terms[0]).to.be.an.instanceof(ast.WordNode); 38 | expect(parsed.terms[1]).to.be.an.instanceof(ast.RelSentNode); 39 | expect(parsed.terms[1].argumentList).to.have.length(1); 40 | expect(parsed.terms[1].argumentList[0]).to.be.an.instanceof(ast.WordNode); 41 | expect(parsed.terms[1].argumentList[0].word).to.equal('argument'); 42 | }); 43 | 44 | it('correctly parses higher-order Equations', function() { 45 | var parsed = Parser.parse('(= firstTerm (= secondFirstTerm firstSecondTerm))').expressions[0]; 46 | expect(parsed.terms).to.have.length(2); 47 | expect(parsed.terms[0]).to.be.an.instanceof(ast.WordNode); 48 | expect(parsed.terms[1]).to.be.an.instanceof(ast.EquationNode); 49 | expect(parsed.terms[1].terms[0]).to.be.an.instanceof(ast.WordNode); 50 | expect(parsed.terms[1].terms[1]).to.be.an.instanceof(ast.WordNode); 51 | expect(parsed.terms[1].terms[0].word).to.equal('secondFirstTerm'); 52 | expect(parsed.terms[1].terms[1].word).to.equal('firstSecondTerm'); 53 | }); 54 | 55 | }); 56 | -------------------------------------------------------------------------------- /test/specs/parser/parse/parser_parse_relsent_test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'), 2 | expect = chai.expect, 3 | Parser = require('../../../../lib/jkif_parser'), 4 | ast = require('../../../../lib/ast_constructors/ast_constructors'); 5 | 6 | 7 | describe('Parser.parse Relational sentence parsing', function() { 8 | 9 | it('correctly parses a RelSent into a RelSentNode', function() { 10 | expect(Parser.parse('(?VARIABLE)').expressions[0]).to.be.an.instanceof(ast.RelSentNode); 11 | }); 12 | 13 | it('correctly parses a RelSent with no arguments', function() { 14 | expect(Parser.parse('(@dog)').expressions[0]).to.be.an.instanceof(ast.RelSentNode); 15 | }); 16 | 17 | it('correctly parses a RelSent argumentList into an Array', function() { 18 | var parsed = Parser.parse('(?variable argument)').expressions[0]; 19 | expect(parsed.argumentList).to.be.an.instanceof(Array); 20 | }); 21 | 22 | it('correctly parses a RelSent variable name', function() { 23 | var parsed = Parser.parse('(?variable argument)').expressions[0]; 24 | expect(parsed.constant.variableName).to.equal('variable'); 25 | }); 26 | 27 | it('correctly parses a RelSent with a single argument', function() { 28 | var parsed = Parser.parse('(?variable argument)').expressions[0]; 29 | expect(parsed).to.be.an.instanceof(ast.RelSentNode); 30 | expect(parsed.argumentList[0]).to.be.an.instanceof(ast.WordNode); 31 | }); 32 | 33 | it('correctly parses a RelSent with two arguments', function() { 34 | var parsed = Parser.parse('(@variable argument secondArg)').expressions[0]; 35 | expect(parsed).to.be.an.instanceof(ast.RelSentNode); 36 | expect(parsed.argumentList.length).to.be.equal(2); 37 | expect(parsed.argumentList[0]).to.be.an.instanceof(ast.WordNode); 38 | }); 39 | 40 | it('correctly parses a higher-order RelSent', function() { 41 | var parsed = Parser.parse('(?VARIABLE argument (?SECONDVARIABLE secondArgument))').expressions[0]; 42 | expect(parsed).to.be.an.instanceof(ast.RelSentNode); 43 | expect(parsed.argumentList).to.have.length(2); 44 | expect(parsed.argumentList[0]).to.be.an.instanceof(ast.WordNode); 45 | expect(parsed.argumentList[1]).to.be.an.instanceof(ast.RelSentNode); 46 | }); 47 | 48 | it('correctly parses FunctionTerms into RelSentNodes', function() { 49 | expect(Parser.parse('(id)').expressions[0]).to.be.an.instanceof(ast.RelSentNode); 50 | }); 51 | 52 | it('correctly parses a FunctionTerm with no arguments', function() { 53 | expect(Parser.parse('(clark)').expressions[0]).to.be.an.instanceof(ast.RelSentNode); 54 | }); 55 | 56 | it('correctly parses a FunctionTerm argumentList into an Array', function() { 57 | var parsed = Parser.parse('(function argument)').expressions[0]; 58 | expect(parsed.argumentList).to.be.an.instanceof(Array); 59 | }); 60 | 61 | it('correctly parses a FunctionTerm constant', function() { 62 | var parsed = Parser.parse('(function argument)').expressions[0]; 63 | var parsed2 = Parser.parse('(?variable argument)').expressions[0]; 64 | expect(parsed.constant).to.be.an.instanceof(ast.WordNode); 65 | expect(parsed2.constant).to.be.an.instanceof(ast.VariableNode); 66 | }); 67 | 68 | it('correctly parses a FunctionTerm with a single argument', function() { 69 | var parsed = Parser.parse('(function argument)').expressions[0]; 70 | expect(parsed).to.be.an.instanceof(ast.RelSentNode); 71 | expect(parsed.argumentList[0]).to.be.an.instanceof(ast.WordNode); 72 | }); 73 | 74 | it('correctly parses a FunctionTerm with two arguments', function() { 75 | var parsed = Parser.parse('(function argument secondArg)').expressions[0]; 76 | expect(parsed).to.be.an.instanceof(ast.RelSentNode); 77 | expect(parsed.argumentList.length).to.be.equal(2); 78 | expect(parsed.argumentList[0]).to.be.an.instanceof(ast.WordNode); 79 | }); 80 | 81 | it('correctly parses a FunctionTerm with a FunctionTerm as an argument', function() { 82 | var parsed = Parser.parse('(function argument (secondfunc argument))').expressions[0]; 83 | expect(parsed).to.be.an.instanceof(ast.RelSentNode); 84 | expect(parsed.argumentList).to.have.length(2); 85 | expect(parsed.argumentList[0]).to.be.an.instanceof(ast.WordNode); 86 | expect(parsed.argumentList[1]).to.be.an.instanceof(ast.RelSentNode); 87 | }); 88 | 89 | }); 90 | -------------------------------------------------------------------------------- /lib/grammars.jison: -------------------------------------------------------------------------------- 1 | /** 2 | * jKif Parser - 2015 3 | * grammars.jison 4 | * @file Language Grammars for SUO-KIF via JavaScript, Flex, and Jison 5 | * @author Clark Feusier - cfeusier@gmail.com 6 | * @copyright Copyright (C) Clark Feusier - All Rights Reserved 7 | */ 8 | 9 | 10 | %{ 11 | var path = require('path'); 12 | var ast = require(path.resolve(__dirname + 13 | './../../../lib/ast_constructors/ast_constructors')); 14 | %} 15 | 16 | %lex 17 | 18 | white \s+ 19 | comment ";" 20 | commentLine {comment}[^\n]* \n? 21 | ignoreable {white}|{commentLine} 22 | initialChar [a-zA-Z] 23 | digit [0-9] 24 | decimalDigits {digit}+ 25 | eE [eE] 26 | exponent {eE}(\-)?{decimalDigits} 27 | separator [\-\_] 28 | anyChar {initialChar}|{digit}|{separator} 29 | special [#^!\$%&\*\+-\.\<=>\?@_~\\] 30 | freeChar {anyChar}|{special}|{white} 31 | stringLiteral (\"{freeChar}*\")|(\'{freeChar}*\') 32 | numericLiteral (\-)?{decimalDigits}("."{decimalDigits})?({exponent})?\b 33 | identifier {initialChar}{anyChar}* 34 | 35 | %options flex yylineno 36 | 37 | %% 38 | {ignoreable} { /* ignore */ } 39 | "(" { return 'LPAREN'; } 40 | ")" { return 'RPAREN'; } 41 | "?" { return 'QUESTION'; } 42 | "@" { return 'MENTION'; } 43 | "=" { return 'EQUALS'; } 44 | ">" { return 'RARROW'; } 45 | "<" { return 'LARROW'; } 46 | "not"|"NOT" { return 'NOT'; } 47 | "or"|"OR" { return 'OR'; } 48 | "and"|"AND" { return 'AND'; } 49 | "forall"|"FORALL" { return 'FORALL'; } 50 | "exists"|"EXISTS" { return 'EXISTS'; } 51 | {stringLiteral} { return 'STRINGLITERAL'; } 52 | {numericLiteral} { return 'NUMERICLITERAL'; } 53 | {identifier} { return 'IDENTIFIER'; } 54 | <> { return 'EOF'; } 55 | %% 56 | 57 | /lex 58 | 59 | 60 | %start KIF 61 | 62 | %% 63 | KIF 64 | : KIFexpressions EOF 65 | { $$ = new ast.KIFNode(@$, $KIFexpressions); return $$; } 66 | ; 67 | 68 | KIFexpressions 69 | : KIFexpressions KIFexpression 70 | { $$ = $KIFexpressions.concat($KIFexpression); } 71 | | 72 | { $$ = []; } 73 | ; 74 | 75 | KIFexpression 76 | : Word 77 | | Variable 78 | | String 79 | | Number 80 | | Sentence 81 | ; 82 | 83 | Sentence 84 | : Equation 85 | | RelSent 86 | | LogicSent 87 | | QuantSent 88 | ; 89 | 90 | LogicSent 91 | : Negation 92 | | Disjunction 93 | | Conjunction 94 | | Implication 95 | | Equivalence 96 | ; 97 | 98 | QuantSent 99 | : UniversalSent 100 | | ExistentialSent 101 | ; 102 | 103 | Word 104 | : IDENTIFIER 105 | { $$ = new ast.WordNode(@$, $IDENTIFIER); } 106 | ; 107 | 108 | Variable 109 | : QUESTION IDENTIFIER 110 | { $$ = new ast.VariableNode(@$, $IDENTIFIER, 'IND'); } 111 | | MENTION IDENTIFIER 112 | { $$ = new ast.VariableNode(@$, $IDENTIFIER, 'ROW'); } 113 | ; 114 | 115 | String 116 | : STRINGLITERAL 117 | { $$ = new ast.StringLiteralNode(@$, $STRINGLITERAL); } 118 | ; 119 | 120 | Number 121 | : NUMERICLITERAL 122 | { $$ = new ast.NumericLiteralNode(@$, $NUMERICLITERAL); } 123 | ; 124 | 125 | ArgumentList 126 | : ArgumentList KIFexpression 127 | { $$ = $ArgumentList.concat($KIFexpression); } 128 | | 129 | { $$ = []; } 130 | ; 131 | 132 | VariableList 133 | : VariableList Variable 134 | { $$ = $VariableList.concat($Variable); } 135 | | Variable 136 | { $$ = [$Variable]; } 137 | ; 138 | 139 | Equation 140 | : LPAREN EQUALS KIFexpression KIFexpression RPAREN 141 | { $$ = new ast.EquationNode(@$, $KIFexpression1, $KIFexpression2); } 142 | ; 143 | 144 | RelSent 145 | : LPAREN Variable ArgumentList RPAREN 146 | { $$ = new ast.RelSentNode(@$, $Variable, $ArgumentList); } 147 | | LPAREN Word ArgumentList RPAREN 148 | { $$ = new ast.RelSentNode(@$, $Word, $ArgumentList); } 149 | ; 150 | 151 | Negation 152 | : LPAREN NOT KIFexpression RPAREN 153 | { $$ = new ast.NegationNode(@$, $KIFexpression); } 154 | ; 155 | 156 | Disjunction 157 | : LPAREN OR ArgumentList RPAREN 158 | { $$ = new ast.DisjunctionNode(@$, $ArgumentList); } 159 | ; 160 | 161 | Conjunction 162 | : LPAREN AND ArgumentList RPAREN 163 | { $$ = new ast.ConjunctionNode(@$, $ArgumentList); } 164 | ; 165 | 166 | Implication 167 | : LPAREN EQUALS RARROW KIFexpression KIFexpression RPAREN 168 | { $$ = new ast.ImplicationNode(@$, $KIFexpression1, $KIFexpression2); } 169 | ; 170 | 171 | Equivalence 172 | : LPAREN LARROW EQUALS RARROW KIFexpression KIFexpression RPAREN 173 | { $$ = new ast.EquivalenceNode(@$, $KIFexpression1, $KIFexpression2); } 174 | ; 175 | 176 | UniversalSent 177 | : LPAREN FORALL LPAREN VariableList RPAREN KIFexpression RPAREN 178 | { $$ = new ast.UniversalSentNode(@$, $VariableList, $KIFexpression); } 179 | ; 180 | 181 | ExistentialSent 182 | : LPAREN EXISTS LPAREN VariableList RPAREN KIFexpression RPAREN 183 | { $$ = new ast.ExistentialSentNode(@$, $VariableList, $KIFexpression); } 184 | ; 185 | %% 186 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jKif Parser 2 | 3 | **jKif Parser** is a SUO-KIF to JavaScript parser, which allows you to easily parse SUO-KIF ontologies into JavaScript objects. 4 | 5 | [ ![Current Stable Release Version](https://img.shields.io/badge/version-1.0.0-blue.svg)](https://github.com/jkif/parser/releases) 6 | [ ![Codeship Status for jkif/parser](https://img.shields.io/badge/build-passing-brightgreen.svg)](https://codeship.com/projects/81732/) 7 | [ ![Current Stable npm Release](https://img.shields.io/badge/npm-install%20jkif--parser-lightgrey.svg)](https://www.npmjs.com/package/jkif-parser) 8 | 9 | --- 10 | 11 | > **Created by [Clark Feusier](http://clarkfeusier.com/pages/about)** during tenure as a hacker in residence at [Hack Reactor](http://hackreactor.com) 12 | 13 | --- 14 | 15 | 1. [Overview](#overview) 16 | 1. [Dependencies](#dependencies) 17 | 1. [Installation](#installation) 18 | 1. [Documentation](#documentation) 19 | 1. [API Reference](#api-reference) 20 | 1. [Roadmap](#roadmap) 21 | 1. [Contributing](#contributing-to-jkif-parser) 22 | 1. [Development Requirements](#development-requirements) 23 | 1. [Installing Dependencies](#installing-dependencies) 24 | 1. [Running Tests](#running-tests) 25 | 1. [License](#license) 26 | 1. [Appendix](#appendix) 27 | 28 | --- 29 | 30 | ## Overview 31 | 32 | The **jKif Parser** facilitates the transmission and growth of formal knowledge by providing JavaScript access to the vast stores of open SUO-KIF ontologies and factbases. 33 | 34 | **Formal knowledge** is good for computers because it can be ***inferred over*** to generate new knowledge, with guaranteed consistency, given consistent input. Such guarantee cannot be made when reasoning over informal taxonomies constructed in RDF/OWL/AIF+, resulting in near uselessness for machine learning purposes. 35 | 36 | **SUO-KIF** is used to symbolize large, formal, open ontologies and factbases like SUMO and MILO. Also, it is easy to **write** your own domain-specific facts or ontologies in SUO-KIF. 37 | 38 | However, access to said open ontologies and factbases is restricted because there are *few quality open-source parsers of SUO-KIF*. 39 | 40 | Enter the **jKif Parser** — providing simple JavaScript access to SUO-KIF, and thus, providing JavaScript access to the formal ontologies and factbases written in SUO-KIF. 41 | 42 | --- 43 | 44 | To learn more about knowledge representation, SUO-KIF, formal ontology, SUMO, Jison, or parsers, please explore the [appendix](#appendix). 45 | 46 | --- 47 | 48 | #### Dependencies 49 | 50 | - [Jison](http://zaach.github.io/jison/docs/) — generates LALR(1) parser 51 | - [Bluebird](https://github.com/petkaantonov/bluebird) — creates a Promise-interface for parser methods 52 | - [JSONFile](https://www.npmjs.com/package/jsonfile) — writes parser output to file in formatted JSON 53 | 54 | --- 55 | 56 | ## Installation 57 | 58 | **jKif Parser** is available as an npm package. 59 | 60 | ***Install module from command-line*** 61 | 62 | ```sh 63 | npm install jkif-parser 64 | ``` 65 | 66 | ***Require module for use in desired file*** 67 | 68 | ```js 69 | var jkParser = require('jkif-parser'); 70 | ``` 71 | 72 | --- 73 | 74 | ## Documentation 75 | 76 | ### *jKif Parser* 77 | 78 | This object provides an object-oriented API for parsing SUO-KIF into JavaScript, as well as utility methods for handling the parsed output. 79 | 80 | ```js 81 | var Parser = require('jkif-parser'); 82 | ``` 83 | 84 | ### API Reference 85 | 86 | - [**`parse`**](#parse) 87 | - [**`parseFile`**](#parseFile) 88 | - [**`parseFileP`**](#parseFileP) 89 | - [**`writeParsedToFile`**](#writeParsedToFile) 90 | - [**`writeParsedToFileP`**](#writeParsedToFileP) 91 | 92 | #### parse 93 | 94 | #### `parse(suoKif: string): KIFNode` 95 | 96 | Synchronously parses string of SUO-KIF into an Abstract Syntax Tree represented by a JavaScript `KIFNode`. 97 | 98 | ```js 99 | Parser.parse('(instance ?FIDDLE Dog)'); 100 | 101 | // Output JavaScript AST 102 | { 103 | type: 'KIFNode', 104 | locationData: { 105 | first_line: 1, 106 | last_line: 1, 107 | first_column: 0, 108 | last_column: 22 109 | }, 110 | expressions: [ 111 | { 112 | type: 'RelSentNode', 113 | locationData: { ... }, 114 | constant: { 115 | type: 'WordNode', 116 | locationData: { ... }, 117 | word: 'instance' 118 | }, 119 | argumentList: [ 120 | { 121 | type: 'VariableNode', 122 | locationData: { ... }, 123 | variableType: 'IND', 124 | variableName: 'FIDDLE' 125 | }, 126 | { 127 | type: 'WordNode', 128 | locationData: { ... }, 129 | word: 'Dog' 130 | } 131 | ] 132 | } 133 | ] 134 | } 135 | ``` 136 | 137 | #### parseFile 138 | 139 | #### `parseFile(filePath: string, cb: function): void` 140 | 141 | Asynchronously parses a file of SUO-KIF into an Abstract Syntax Tree represented by a JavaScript `KIFNode`, which is then passed to the callback function on invocation. 142 | 143 | The callback function will receive two arguments — an `error` and a `KIFNode` (an AST of the parsed file). The `error` will be null if the parsing was successful. 144 | 145 | ```js 146 | Parser.parseFile('filePathToSomeSUOKIF', function(error, kifNode) { 147 | if (!error) { 148 | // do something with the kifNode AST 149 | } 150 | }); 151 | ``` 152 | 153 | **N.B.** — this is a side-effect function, which returns `undefined`. 154 | 155 | #### parseFileP 156 | 157 | #### `parseFileP(filePath: string): Promise` 158 | 159 | Asynchronously parses a file of SUO-KIF into an Abstract Syntax Tree represented by a JavaScript `KIFNode`, which is then used as the resolution of the `parseFileP` promise. 160 | 161 | To access the output of the parsing, register a `then` handler on the promise. 162 | 163 | If the parsing fails, the error can be handled by registering a `catch` handler on the promise. 164 | 165 | ```js 166 | Parser.parseFileP('filePathToSomeSUOKIF').then(function(kifNode) { 167 | // do something with the kifNode AST 168 | }).catch(function(error) { 169 | // do something with the error if the parsing fails 170 | }); 171 | ``` 172 | 173 | #### writeParsedToFile 174 | 175 | #### `writeParsedToFile(filePath: string, parsed: KIFNode, cb: function): void` 176 | 177 | Asynchronously writes *parsed* SUO-KIF to a file, invoking the supplied callback function with the results of the write operation. 178 | 179 | The callback function will receive one argument — an `error`. The `error` will be null if the parsing was successful. 180 | 181 | ```js 182 | var kifString = '(exists (?FIDDLE ?CLARK) 183 | (and 184 | (instance ?FIDDLE Dog) 185 | (loves ?FIDDLE ?CLARK)))'; 186 | var parsed = Parser.parse(kifString); 187 | 188 | Parser.writeParsedToFile('filePath', parsed, function(error) { 189 | if (!error) { 190 | // your file should now have the AST in JSON format 191 | } 192 | }); 193 | ``` 194 | 195 | **N.B.** — this is a side-effect function, which returns `undefined`. 196 | 197 | #### writeParsedToFileP 198 | 199 | #### `writeParsedToFileP(filePath: string, parsed: KIFNode): Promise` 200 | 201 | Asynchronously writes *parsed* SUO-KIF to a file, returning a promise. 202 | 203 | If the write operation is successful, then the promise value will resolve as `null`. If the write operation fails, you can register a `catch` handler function to receive the `error` from the promise resolution. 204 | 205 | ```js 206 | var kifString = '(exists (?FIDDLE ?CLARK) 207 | (and 208 | (instance ?FIDDLE Dog) 209 | (loves ?FIDDLE ?CLARK)))'; 210 | var parsed = Parser.parse(kifString); 211 | 212 | Parser.writeParsedToFileP('filePath', parsed).catch(function(error) { 213 | // handle the error 214 | // if this is not run, the parsed was written to the file successfully 215 | }); 216 | ``` 217 | 218 | --- 219 | 220 | ## Roadmap 221 | 222 | The future of jKif Parser is managed through this repository's **Issues** — [view the roadmap here](https://github.com/jkif/parser/issues). 223 | 224 | ## Contributing to jKif Parser 225 | 226 | We welcome contributions, but please read our [contribution guidelines](CONTRIBUTING.md) before submitting your work. The development requirements and instructions are below. 227 | 228 | ## Development Requirements 229 | 230 | - Node 0.10.x 231 | - npm 2.x.x 232 | - Mocha 233 | - Chai 234 | - Jison 235 | - Bluebird 236 | - JSONFile 237 | 238 | ### Installing Dependencies 239 | 240 | Install Node (bundled with npm) using [Homebrew](http://brew.sh/): 241 | 242 | ```sh 243 | brew install node 244 | ``` 245 | 246 | Install project and development dependencies using npm: 247 | 248 | ```sh 249 | npm install 250 | ``` 251 | 252 | ### Running Tests 253 | 254 | After installing the above dependencies, tests can be run using the following command: 255 | 256 | ```sh 257 | npm test 258 | ``` 259 | 260 | ## License 261 | 262 | jKif Parser - Lexical Analysis and Parsing of SUO-KIF into JavaScript Objects 263 | 264 | Copyright (C) 2015 Clark Feusier - All Rights Reserved 265 | 266 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 267 | 268 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 269 | 270 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 271 | 272 | --- 273 | 274 | ## Appendix 275 | 276 | #### SUO-KIF 277 | 278 | [SUO-KIF] [1] was derived from [KIF] [2] by [Adam Pease] [3] and [Ian Niles] [4] for the construction of [SUMO] [5]. KIF, the Knowledge Interchange Format, is an Erlang-based language used for the formal representation and interchange of knowledge. KIF and SUO-KIF have **declarative semantics** and are **logically complete**, contra languages like *Prolog* and *SQL*. SUO-KIF was designed primarily for the ***authoring*** of knowledge, which makes it more amenable to ontology design than vanilla KIF. 279 | 280 | [1]: http://sigmakee.cvs.sourceforge.net/viewvc/sigmakee/sigma/suo-kif.pdf "SUO-KIF" 281 | [2]: https://www.cs.auckland.ac.nz/courses/compsci367s2c/resources/kif.pdf "KIF" 282 | [3]: http://www.adampease.org/professional/ "Adam Pease" 283 | [4]: https://www.linkedin.com/pub/ian-niles/2/1b6/a69 "Ian Niles" 284 | [5]: http://www.adampease.org/OP/ "SUMO" 285 | 286 | #### Ontologies and SUMO 287 | 288 | The market-wide move, from the *informal* taxonomies of the 'semantic web' to the *formal* ontologies of the new 'cognitive web', is a strong indicator — even small sets of axiomatized knowledge are more powerful than large bodies of informally structured data. 289 | 290 | **Why?** Formal knowledge can be used to generate new knowledge; informal specifications can do no such thing because there is a possibility for inconsistency in the specifications. If we can provide a consistent semantics to our concepts and data, then meanings are not dependent on a particular inference implementation — enter **maching learning**. 291 | 292 | If formal knowledge is good, than open formal knowledge is better, and more open formal knowledge is best. This is the reasoning that led me to choose SUO-KIF as the origin language for the **jKif Parser**. 293 | 294 | The largest formal public ontology in existence today, the **Suggested Upper Merged Ontology** (SUMO), is written in SUO-KIF. SUMO is the *only* formal ontology to be mapped to the complete WordNet lexicon. SUMO, and its domain-specific ontologies, consists of over 25,000 terms and more than 80,000 axioms. SUMO has been merged with millions of instance facts from YAGO (Wikipedia). Finally, SUMO is free and owned by the IEEE. 295 | 296 | I want to get SUMO into the hands of the world's engineers — JavaScript seemed like a logical choice for a target language on top of which to expose an API for querying and manipulating SUO-KIF (the next library jKif will release). 297 | 298 | #### Jison and Parser Generators 299 | 300 | [**Jison**](http://zaach.github.io/jison/docs/) is a JavaScript parser generator, based closely on the famous Yacc and Bison. Jison also includes a lexical analyzer that is very similar to Lex/Flex. Jison is probably most well-known for its use in generation of the parsers used in the CoffeeScript and handlebars.js compilers. 301 | 302 | Jison, like most parser generators, takes a lexical scanner and [**context-free grammar**](http://en.wikipedia.org/wiki/Context-free_grammar) as input, and spits out a parser that can be used to parse the langauge described by the input grammar. 303 | 304 | The generated parser algorithm is an LALR(1) **shift-reduce** algorithm — shifting tokens onto a parse stack until a rule is recognized, at which point the matching tokens are reduced to the result of a combination action described by the matched rule. This is a **bottom-up** approach to parsing, keeping a single look-ahead token, as described [here](http://dinosaur.compilertools.net/bison/bison_8.html#SEC68). 305 | 306 | #### [Back to Top](#) 307 | -------------------------------------------------------------------------------- /STYLE_GUIDE.md: -------------------------------------------------------------------------------- 1 | # JavaScript Style Guide 2 | 3 | *Forked from [Airbnb's Style Guide](https://github.com/airbnb/javascript)* 4 | 5 | ## Table of Contents 6 | 7 | 1. [Types](#types) 8 | 1. [Objects](#objects) 9 | 1. [Arrays](#arrays) 10 | 1. [Strings](#strings) 11 | 1. [Functions](#functions) 12 | 1. [Properties](#properties) 13 | 1. [Variables](#variables) 14 | 1. [Hoisting](#hoisting) 15 | 1. [Conditional Expressions & Equality](#conditional-expressions--equality) 16 | 1. [Blocks](#blocks) 17 | 1. [Comments](#comments) 18 | 1. [Whitespace](#whitespace) 19 | 1. [Commas](#commas) 20 | 1. [Semicolons](#semicolons) 21 | 1. [Type Casting & Coercion](#type-casting--coercion) 22 | 1. [Naming Conventions](#naming-conventions) 23 | 1. [Accessors](#accessors) 24 | 1. [Constructors](#constructors) 25 | 1. [Events](#events) 26 | 1. [Modules](#modules) 27 | 1. [jQuery](#jquery) 28 | 1. [ECMAScript 5 Compatibility](#ecmascript-5-compatibility) 29 | 1. [Testing](#testing) 30 | 31 | ## Types 32 | 33 | - **Primitives**: When you access a primitive type you work directly on its value 34 | 35 | + `string` 36 | + `number` 37 | + `boolean` 38 | + `null` 39 | + `undefined` 40 | 41 | ```javascript 42 | var foo = 1; 43 | var bar = foo; 44 | 45 | bar = 9; 46 | 47 | console.log(foo, bar); // => 1, 9 48 | ``` 49 | - **Complex**: When you access a complex type you work on a reference to its value 50 | 51 | + `object` 52 | + `array` 53 | + `function` 54 | 55 | ```javascript 56 | var foo = [1, 2]; 57 | var bar = foo; 58 | 59 | bar[0] = 9; 60 | 61 | console.log(foo[0], bar[0]); // => 9, 9 62 | ``` 63 | 64 | **[⬆ back to top](#table-of-contents)** 65 | 66 | ## Objects 67 | 68 | - Use the literal syntax for object creation. 69 | 70 | ```javascript 71 | // bad 72 | var item = new Object(); 73 | 74 | // good 75 | var item = {}; 76 | ``` 77 | 78 | - Don't use [reserved words](http://es5.github.io/#x7.6.1) as keys. It won't work in IE8. [More info](https://github.com/airbnb/javascript/issues/61) 79 | 80 | ```javascript 81 | // bad 82 | var superman = { 83 | default: { clark: 'kent' }, 84 | private: true 85 | }; 86 | 87 | // good 88 | var superman = { 89 | defaults: { clark: 'kent' }, 90 | hidden: true 91 | }; 92 | ``` 93 | 94 | - Use readable synonyms in place of reserved words. 95 | 96 | ```javascript 97 | // bad 98 | var superman = { 99 | class: 'alien' 100 | }; 101 | 102 | // bad 103 | var superman = { 104 | klass: 'alien' 105 | }; 106 | 107 | // good 108 | var superman = { 109 | type: 'alien' 110 | }; 111 | ``` 112 | 113 | **[⬆ back to top](#table-of-contents)** 114 | 115 | ## Arrays 116 | 117 | - Use the literal syntax for array creation 118 | 119 | ```javascript 120 | // bad 121 | var items = new Array(); 122 | 123 | // good 124 | var items = []; 125 | ``` 126 | 127 | - If you don't know array length use Array#push. 128 | 129 | ```javascript 130 | var someStack = []; 131 | 132 | 133 | // bad 134 | someStack[someStack.length] = 'abracadabra'; 135 | 136 | // good 137 | someStack.push('abracadabra'); 138 | ``` 139 | 140 | - When you need to copy an array use Array#slice. [jsPerf](http://jsperf.com/converting-arguments-to-an-array/7) 141 | 142 | ```javascript 143 | var len = items.length; 144 | var itemsCopy = []; 145 | var i; 146 | 147 | // bad 148 | for (i = 0; i < len; i++) { 149 | itemsCopy[i] = items[i]; 150 | } 151 | 152 | // good 153 | itemsCopy = items.slice(); 154 | ``` 155 | 156 | - To convert an array-like object to an array, use Array#slice. 157 | 158 | ```javascript 159 | function trigger() { 160 | var args = Array.prototype.slice.call(arguments); 161 | ... 162 | } 163 | ``` 164 | 165 | **[⬆ back to top](#table-of-contents)** 166 | 167 | 168 | ## Strings 169 | 170 | - Use single quotes `''` for strings 171 | 172 | ```javascript 173 | // bad 174 | var name = "Bob Parr"; 175 | 176 | // good 177 | var name = 'Bob Parr'; 178 | 179 | // bad 180 | var fullName = "Bob " + this.lastName; 181 | 182 | // good 183 | var fullName = 'Bob ' + this.lastName; 184 | ``` 185 | 186 | - Strings longer than 80 characters should be written across multiple lines using string concatenation. 187 | - Note: If overused, long strings with concatenation could impact performance. [jsPerf](http://jsperf.com/ya-string-concat) & [Discussion](https://github.com/airbnb/javascript/issues/40) 188 | 189 | ```javascript 190 | // bad 191 | var errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.'; 192 | 193 | // bad 194 | var errorMessage = 'This is a super long error that was thrown because \ 195 | of Batman. When you stop to think about how Batman had anything to do \ 196 | with this, you would get nowhere \ 197 | fast.'; 198 | 199 | // good 200 | var errorMessage = 'This is a super long error that was thrown because ' + 201 | 'of Batman. When you stop to think about how Batman had anything to do ' + 202 | 'with this, you would get nowhere fast.'; 203 | ``` 204 | 205 | - When programmatically building up a string, use Array#join instead of string concatenation. Mostly for IE: [jsPerf](http://jsperf.com/string-vs-array-concat/2). 206 | 207 | ```javascript 208 | var items; 209 | var messages; 210 | var length; 211 | var i; 212 | 213 | messages = [{ 214 | state: 'success', 215 | message: 'This one worked.' 216 | }, { 217 | state: 'success', 218 | message: 'This one worked as well.' 219 | }, { 220 | state: 'error', 221 | message: 'This one did not work.' 222 | }]; 223 | 224 | length = messages.length; 225 | 226 | // bad 227 | function inbox(messages) { 228 | items = '
    '; 229 | 230 | for (i = 0; i < length; i++) { 231 | items += '
  • ' + messages[i].message + '
  • '; 232 | } 233 | 234 | return items + '
'; 235 | } 236 | 237 | // good 238 | function inbox(messages) { 239 | items = []; 240 | 241 | for (i = 0; i < length; i++) { 242 | items[i] = messages[i].message; 243 | } 244 | 245 | return '
  • ' + items.join('
  • ') + '
'; 246 | } 247 | ``` 248 | 249 | **[⬆ back to top](#table-of-contents)** 250 | 251 | 252 | ## Functions 253 | 254 | - Function expressions: 255 | 256 | ```javascript 257 | // anonymous function expression 258 | var anonymous = function() { 259 | return true; 260 | }; 261 | 262 | // named function expression 263 | var named = function named() { 264 | return true; 265 | }; 266 | 267 | // immediately-invoked function expression (IIFE) 268 | (function() { 269 | console.log('Welcome to the Internet. Please follow me.'); 270 | })(); 271 | ``` 272 | 273 | - Never declare a function in a non-function block (if, while, etc). Assign the function to a variable instead. Browsers will allow you to do it, but they all interpret it differently, which is bad news bears. 274 | - **Note:** ECMA-262 defines a `block` as a list of statements. A function declaration is not a statement. [Read ECMA-262's note on this issue](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf#page=97). 275 | 276 | ```javascript 277 | // bad 278 | if (currentUser) { 279 | function test() { 280 | console.log('Nope.'); 281 | } 282 | } 283 | 284 | // good 285 | var test; 286 | if (currentUser) { 287 | test = function test() { 288 | console.log('Yup.'); 289 | }; 290 | } 291 | ``` 292 | 293 | - Never name a parameter `arguments`, this will take precedence over the `arguments` object that is given to every function scope. 294 | 295 | ```javascript 296 | // bad 297 | function nope(name, options, arguments) { 298 | // ...stuff... 299 | } 300 | 301 | // good 302 | function yup(name, options, args) { 303 | // ...stuff... 304 | } 305 | ``` 306 | 307 | **[⬆ back to top](#table-of-contents)** 308 | 309 | 310 | 311 | ## Properties 312 | 313 | - Use dot notation when accessing properties. 314 | 315 | ```javascript 316 | var luke = { 317 | jedi: true, 318 | age: 28 319 | }; 320 | 321 | // bad 322 | var isJedi = luke['jedi']; 323 | 324 | // good 325 | var isJedi = luke.jedi; 326 | ``` 327 | 328 | - Use subscript notation `[]` when accessing properties with a variable. 329 | 330 | ```javascript 331 | var luke = { 332 | jedi: true, 333 | age: 28 334 | }; 335 | 336 | function getProp(prop) { 337 | return luke[prop]; 338 | } 339 | 340 | var isJedi = getProp('jedi'); 341 | ``` 342 | 343 | **[⬆ back to top](#table-of-contents)** 344 | 345 | 346 | ## Variables 347 | 348 | - Always use `var` to declare variables. Not doing so will result in global variables. We want to avoid polluting the global namespace. Captain Planet warned us of that. 349 | 350 | ```javascript 351 | // bad 352 | superPower = new SuperPower(); 353 | 354 | // good 355 | var superPower = new SuperPower(); 356 | ``` 357 | 358 | - Use one `var` declaration per variable. 359 | It's easier to add new variable declarations this way, and you never have 360 | to worry about swapping out a `;` for a `,` or introducing punctuation-only 361 | diffs. 362 | 363 | ```javascript 364 | // bad 365 | var items = getItems(), 366 | goSportsTeam = true, 367 | dragonball = 'z'; 368 | 369 | // bad 370 | // (compare to above, and try to spot the mistake) 371 | var items = getItems(), 372 | goSportsTeam = true; 373 | dragonball = 'z'; 374 | 375 | // good 376 | var items = getItems(); 377 | var goSportsTeam = true; 378 | var dragonball = 'z'; 379 | ``` 380 | 381 | - Declare unassigned variables last. This is helpful when later on you might need to assign a variable depending on one of the previous assigned variables. 382 | 383 | ```javascript 384 | // bad 385 | var i, len, dragonball, 386 | items = getItems(), 387 | goSportsTeam = true; 388 | 389 | // bad 390 | var i; 391 | var items = getItems(); 392 | var dragonball; 393 | var goSportsTeam = true; 394 | var len; 395 | 396 | // good 397 | var items = getItems(); 398 | var goSportsTeam = true; 399 | var dragonball; 400 | var length; 401 | var i; 402 | ``` 403 | 404 | - Assign variables at the top of their scope. This helps avoid issues with variable declaration and assignment hoisting related issues. 405 | 406 | ```javascript 407 | // bad 408 | function() { 409 | test(); 410 | console.log('doing stuff..'); 411 | 412 | //..other stuff.. 413 | 414 | var name = getName(); 415 | 416 | if (name === 'test') { 417 | return false; 418 | } 419 | 420 | return name; 421 | } 422 | 423 | // good 424 | function() { 425 | var name = getName(); 426 | 427 | test(); 428 | console.log('doing stuff..'); 429 | 430 | //..other stuff.. 431 | 432 | if (name === 'test') { 433 | return false; 434 | } 435 | 436 | return name; 437 | } 438 | 439 | // bad 440 | function() { 441 | var name = getName(); 442 | 443 | if (!arguments.length) { 444 | return false; 445 | } 446 | 447 | return true; 448 | } 449 | 450 | // good 451 | function() { 452 | if (!arguments.length) { 453 | return false; 454 | } 455 | 456 | var name = getName(); 457 | 458 | return true; 459 | } 460 | ``` 461 | 462 | **[⬆ back to top](#table-of-contents)** 463 | 464 | 465 | ## Hoisting 466 | 467 | - Variable declarations get hoisted to the top of their scope, their assignment does not. 468 | 469 | ```javascript 470 | // we know this wouldn't work (assuming there 471 | // is no notDefined global variable) 472 | function example() { 473 | console.log(notDefined); // => throws a ReferenceError 474 | } 475 | 476 | // creating a variable declaration after you 477 | // reference the variable will work due to 478 | // variable hoisting. Note: the assignment 479 | // value of `true` is not hoisted. 480 | function example() { 481 | console.log(declaredButNotAssigned); // => undefined 482 | var declaredButNotAssigned = true; 483 | } 484 | 485 | // The interpreter is hoisting the variable 486 | // declaration to the top of the scope. 487 | // Which means our example could be rewritten as: 488 | function example() { 489 | var declaredButNotAssigned; 490 | console.log(declaredButNotAssigned); // => undefined 491 | declaredButNotAssigned = true; 492 | } 493 | ``` 494 | 495 | - Anonymous function expressions hoist their variable name, but not the function assignment. 496 | 497 | ```javascript 498 | function example() { 499 | console.log(anonymous); // => undefined 500 | 501 | anonymous(); // => TypeError anonymous is not a function 502 | 503 | var anonymous = function() { 504 | console.log('anonymous function expression'); 505 | }; 506 | } 507 | ``` 508 | 509 | - Named function expressions hoist the variable name, not the function name or the function body. 510 | 511 | ```javascript 512 | function example() { 513 | console.log(named); // => undefined 514 | 515 | named(); // => TypeError named is not a function 516 | 517 | superPower(); // => ReferenceError superPower is not defined 518 | 519 | var named = function superPower() { 520 | console.log('Flying'); 521 | }; 522 | } 523 | 524 | // the same is true when the function name 525 | // is the same as the variable name. 526 | function example() { 527 | console.log(named); // => undefined 528 | 529 | named(); // => TypeError named is not a function 530 | 531 | var named = function named() { 532 | console.log('named'); 533 | } 534 | } 535 | ``` 536 | 537 | - Function declarations hoist their name and the function body. 538 | 539 | ```javascript 540 | function example() { 541 | superPower(); // => Flying 542 | 543 | function superPower() { 544 | console.log('Flying'); 545 | } 546 | } 547 | ``` 548 | 549 | - For more information refer to [JavaScript Scoping & Hoisting](http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting) by [Ben Cherry](http://www.adequatelygood.com/) 550 | 551 | **[⬆ back to top](#table-of-contents)** 552 | 553 | 554 | 555 | ## Conditional Expressions & Equality 556 | 557 | - Use `===` and `!==` over `==` and `!=`. 558 | - Conditional expressions are evaluated using coercion with the `ToBoolean` method and always follow these simple rules: 559 | 560 | + **Objects** evaluate to **true** 561 | + **Undefined** evaluates to **false** 562 | + **Null** evaluates to **false** 563 | + **Booleans** evaluate to **the value of the boolean** 564 | + **Numbers** evaluate to **false** if **+0, -0, or NaN**, otherwise **true** 565 | + **Strings** evaluate to **false** if an empty string `''`, otherwise **true** 566 | 567 | ```javascript 568 | if ([0]) { 569 | // true 570 | // An array is an object, objects evaluate to true 571 | } 572 | ``` 573 | 574 | - Use shortcuts. 575 | 576 | ```javascript 577 | // bad 578 | if (name !== '') { 579 | // ...stuff... 580 | } 581 | 582 | // good 583 | if (name) { 584 | // ...stuff... 585 | } 586 | 587 | // bad 588 | if (collection.length > 0) { 589 | // ...stuff... 590 | } 591 | 592 | // good 593 | if (collection.length) { 594 | // ...stuff... 595 | } 596 | ``` 597 | 598 | - For more information see [Truth Equality and JavaScript](http://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/#more-2108) by Angus Croll 599 | 600 | **[⬆ back to top](#table-of-contents)** 601 | 602 | 603 | ## Blocks 604 | 605 | - Use braces with all multi-line blocks. 606 | 607 | ```javascript 608 | // bad 609 | if (test) 610 | return false; 611 | 612 | // good 613 | if (test) return false; 614 | 615 | // good 616 | if (test) { 617 | return false; 618 | } 619 | 620 | // bad 621 | function() { return false; } 622 | 623 | // good 624 | function() { 625 | return false; 626 | } 627 | ``` 628 | 629 | **[⬆ back to top](#table-of-contents)** 630 | 631 | 632 | ## Comments 633 | 634 | - Use `/** ... */` for multiline comments. Include a description, specify types and values for all parameters and return values. 635 | 636 | ```javascript 637 | // bad 638 | // make() returns a new element 639 | // based on the passed in tag name 640 | // 641 | // @param {String} tag 642 | // @return {Element} element 643 | function make(tag) { 644 | 645 | // ...stuff... 646 | 647 | return element; 648 | } 649 | 650 | // good 651 | /** 652 | * make() returns a new element 653 | * based on the passed in tag name 654 | * 655 | * @param {String} tag 656 | * @return {Element} element 657 | */ 658 | function make(tag) { 659 | 660 | // ...stuff... 661 | 662 | return element; 663 | } 664 | ``` 665 | 666 | - Use `//` for single line comments. Place single line comments on a newline above the subject of the comment. Put an empty line before the comment. 667 | 668 | ```javascript 669 | // bad 670 | var active = true; // is current tab 671 | 672 | // good 673 | // is current tab 674 | var active = true; 675 | 676 | // bad 677 | function getType() { 678 | console.log('fetching type...'); 679 | // set the default type to 'no type' 680 | var type = this._type || 'no type'; 681 | 682 | return type; 683 | } 684 | 685 | // good 686 | function getType() { 687 | console.log('fetching type...'); 688 | 689 | // set the default type to 'no type' 690 | var type = this._type || 'no type'; 691 | 692 | return type; 693 | } 694 | ``` 695 | 696 | - Prefixing your comments with `FIXME` or `TODO` helps other developers quickly understand if you're pointing out a problem that needs to be revisited, or if you're suggesting a solution to the problem that needs to be implemented. These are different than regular comments because they are actionable. The actions are `FIXME -- need to figure this out` or `TODO -- need to implement`. 697 | 698 | - Use `// FIXME:` to annotate problems 699 | 700 | ```javascript 701 | function Calculator() { 702 | 703 | // FIXME: shouldn't use a global here 704 | total = 0; 705 | 706 | return this; 707 | } 708 | ``` 709 | 710 | - Use `// TODO:` to annotate solutions to problems 711 | 712 | ```javascript 713 | function Calculator() { 714 | 715 | // TODO: total should be configurable by an options param 716 | this.total = 0; 717 | 718 | return this; 719 | } 720 | ``` 721 | 722 | **[⬆ back to top](#table-of-contents)** 723 | 724 | 725 | ## Whitespace 726 | 727 | - Use soft tabs set to 2 spaces 728 | 729 | ```javascript 730 | // bad 731 | function() { 732 | ∙∙∙∙var name; 733 | } 734 | 735 | // bad 736 | function() { 737 | ∙var name; 738 | } 739 | 740 | // good 741 | function() { 742 | ∙∙var name; 743 | } 744 | ``` 745 | 746 | - Place 1 space before the leading brace. 747 | 748 | ```javascript 749 | // bad 750 | function test(){ 751 | console.log('test'); 752 | } 753 | 754 | // good 755 | function test() { 756 | console.log('test'); 757 | } 758 | 759 | // bad 760 | dog.set('attr',{ 761 | age: '1 year', 762 | breed: 'Bernese Mountain Dog' 763 | }); 764 | 765 | // good 766 | dog.set('attr', { 767 | age: '1 year', 768 | breed: 'Bernese Mountain Dog' 769 | }); 770 | ``` 771 | 772 | - Set off operators with spaces. 773 | 774 | ```javascript 775 | // bad 776 | var x=y+5; 777 | 778 | // good 779 | var x = y + 5; 780 | ``` 781 | 782 | - End files with a single newline character. 783 | 784 | ```javascript 785 | // bad 786 | (function(global) { 787 | // ...stuff... 788 | })(this); 789 | ``` 790 | 791 | ```javascript 792 | // bad 793 | (function(global) { 794 | // ...stuff... 795 | })(this);↵ 796 | ↵ 797 | ``` 798 | 799 | ```javascript 800 | // good 801 | (function(global) { 802 | // ...stuff... 803 | })(this);↵ 804 | ``` 805 | 806 | - Use indentation when making long method chains. 807 | 808 | ```javascript 809 | // bad 810 | $('#items').find('.selected').highlight().end().find('.open').updateCount(); 811 | 812 | // good 813 | $('#items') 814 | .find('.selected') 815 | .highlight() 816 | .end() 817 | .find('.open') 818 | .updateCount(); 819 | 820 | // bad 821 | var leds = stage.selectAll('.led').data(data).enter().append('svg:svg').class('led', true) 822 | .attr('width', (radius + margin) * 2).append('svg:g') 823 | .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') 824 | .call(tron.led); 825 | 826 | // good 827 | var leds = stage.selectAll('.led') 828 | .data(data) 829 | .enter().append('svg:svg') 830 | .class('led', true) 831 | .attr('width', (radius + margin) * 2) 832 | .append('svg:g') 833 | .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') 834 | .call(tron.led); 835 | ``` 836 | 837 | **[⬆ back to top](#table-of-contents)** 838 | 839 | ## Commas 840 | 841 | - Leading commas: **Nope.** 842 | 843 | ```javascript 844 | // bad 845 | var story = [ 846 | once 847 | , upon 848 | , aTime 849 | ]; 850 | 851 | // good 852 | var story = [ 853 | once, 854 | upon, 855 | aTime 856 | ]; 857 | 858 | // bad 859 | var hero = { 860 | firstName: 'Bob' 861 | , lastName: 'Parr' 862 | , heroName: 'Mr. Incredible' 863 | , superPower: 'strength' 864 | }; 865 | 866 | // good 867 | var hero = { 868 | firstName: 'Bob', 869 | lastName: 'Parr', 870 | heroName: 'Mr. Incredible', 871 | superPower: 'strength' 872 | }; 873 | ``` 874 | 875 | - Additional trailing comma: **Nope.** This can cause problems with IE6/7 and IE9 if it's in quirksmode. Also, in some implementations of ES3 would add length to an array if it had an additional trailing comma. This was clarified in ES5 ([source](http://es5.github.io/#D)): 876 | 877 | > Edition 5 clarifies the fact that a trailing comma at the end of an ArrayInitialiser does not add to the length of the array. This is not a semantic change from Edition 3 but some implementations may have previously misinterpreted this. 878 | 879 | ```javascript 880 | // bad 881 | var hero = { 882 | firstName: 'Kevin', 883 | lastName: 'Flynn', 884 | }; 885 | 886 | var heroes = [ 887 | 'Batman', 888 | 'Superman', 889 | ]; 890 | 891 | // good 892 | var hero = { 893 | firstName: 'Kevin', 894 | lastName: 'Flynn' 895 | }; 896 | 897 | var heroes = [ 898 | 'Batman', 899 | 'Superman' 900 | ]; 901 | ``` 902 | 903 | **[⬆ back to top](#table-of-contents)** 904 | 905 | 906 | ## Semicolons 907 | 908 | - **Yup.** 909 | 910 | ```javascript 911 | // bad 912 | (function() { 913 | var name = 'Skywalker' 914 | return name 915 | })() 916 | 917 | // good 918 | (function() { 919 | var name = 'Skywalker'; 920 | return name; 921 | })(); 922 | 923 | // good (guards against the function becoming an argument when two files with IIFEs are concatenated) 924 | ;(function() { 925 | var name = 'Skywalker'; 926 | return name; 927 | })(); 928 | ``` 929 | 930 | [Read more](http://stackoverflow.com/a/7365214/1712802). 931 | 932 | **[⬆ back to top](#table-of-contents)** 933 | 934 | 935 | ## Type Casting & Coercion 936 | 937 | - Perform type coercion at the beginning of the statement. 938 | - Strings: 939 | 940 | ```javascript 941 | // => this.reviewScore = 9; 942 | 943 | // bad 944 | var totalScore = this.reviewScore + ''; 945 | 946 | // good 947 | var totalScore = '' + this.reviewScore; 948 | 949 | // bad 950 | var totalScore = '' + this.reviewScore + ' total score'; 951 | 952 | // good 953 | var totalScore = this.reviewScore + ' total score'; 954 | ``` 955 | 956 | - Use `parseInt` for Numbers and always with a radix for type casting. 957 | 958 | ```javascript 959 | var inputValue = '4'; 960 | 961 | // bad 962 | var val = new Number(inputValue); 963 | 964 | // bad 965 | var val = +inputValue; 966 | 967 | // bad 968 | var val = inputValue >> 0; 969 | 970 | // bad 971 | var val = parseInt(inputValue); 972 | 973 | // good 974 | var val = Number(inputValue); 975 | 976 | // good 977 | var val = parseInt(inputValue, 10); 978 | ``` 979 | 980 | - If for whatever reason you are doing something wild and `parseInt` is your bottleneck and need to use Bitshift for [performance reasons](http://jsperf.com/coercion-vs-casting/3), leave a comment explaining why and what you're doing. 981 | 982 | ```javascript 983 | // good 984 | /** 985 | * parseInt was the reason my code was slow. 986 | * Bitshifting the String to coerce it to a 987 | * Number made it a lot faster. 988 | */ 989 | var val = inputValue >> 0; 990 | ``` 991 | 992 | - **Note:** Be careful when using bitshift operations. Numbers are represented as [64-bit values](http://es5.github.io/#x4.3.19), but Bitshift operations always return a 32-bit integer ([source](http://es5.github.io/#x11.7)). Bitshift can lead to unexpected behavior for integer values larger than 32 bits. [Discussion](https://github.com/airbnb/javascript/issues/109). Largest signed 32-bit Int is 2,147,483,647: 993 | 994 | ```javascript 995 | 2147483647 >> 0 //=> 2147483647 996 | 2147483648 >> 0 //=> -2147483648 997 | 2147483649 >> 0 //=> -2147483647 998 | ``` 999 | 1000 | - Booleans: 1001 | 1002 | ```javascript 1003 | var age = 0; 1004 | 1005 | // bad 1006 | var hasAge = new Boolean(age); 1007 | 1008 | // good 1009 | var hasAge = Boolean(age); 1010 | 1011 | // good 1012 | var hasAge = !!age; 1013 | ``` 1014 | 1015 | **[⬆ back to top](#table-of-contents)** 1016 | 1017 | 1018 | ## Naming Conventions 1019 | 1020 | - Avoid single letter names. Be descriptive with your naming. 1021 | 1022 | ```javascript 1023 | // bad 1024 | function q() { 1025 | // ...stuff... 1026 | } 1027 | 1028 | // good 1029 | function query() { 1030 | // ..stuff.. 1031 | } 1032 | ``` 1033 | 1034 | - Use camelCase when naming objects, functions, and instances 1035 | 1036 | ```javascript 1037 | // bad 1038 | var OBJEcttsssss = {}; 1039 | var this_is_my_object = {}; 1040 | function c() {} 1041 | var u = new user({ 1042 | name: 'Bob Parr' 1043 | }); 1044 | 1045 | // good 1046 | var thisIsMyObject = {}; 1047 | function thisIsMyFunction() {} 1048 | var user = new User({ 1049 | name: 'Bob Parr' 1050 | }); 1051 | ``` 1052 | 1053 | - Use PascalCase when naming constructors or classes 1054 | 1055 | ```javascript 1056 | // bad 1057 | function user(options) { 1058 | this.name = options.name; 1059 | } 1060 | 1061 | var bad = new user({ 1062 | name: 'nope' 1063 | }); 1064 | 1065 | // good 1066 | function User(options) { 1067 | this.name = options.name; 1068 | } 1069 | 1070 | var good = new User({ 1071 | name: 'yup' 1072 | }); 1073 | ``` 1074 | 1075 | - Use a leading underscore `_` when naming private properties 1076 | 1077 | ```javascript 1078 | // bad 1079 | this.__firstName__ = 'Panda'; 1080 | this.firstName_ = 'Panda'; 1081 | 1082 | // good 1083 | this._firstName = 'Panda'; 1084 | ``` 1085 | 1086 | - When saving a reference to `this` use `_this`. 1087 | 1088 | ```javascript 1089 | // bad 1090 | function() { 1091 | var self = this; 1092 | return function() { 1093 | console.log(self); 1094 | }; 1095 | } 1096 | 1097 | // bad 1098 | function() { 1099 | var that = this; 1100 | return function() { 1101 | console.log(that); 1102 | }; 1103 | } 1104 | 1105 | // good 1106 | function() { 1107 | var _this = this; 1108 | return function() { 1109 | console.log(_this); 1110 | }; 1111 | } 1112 | ``` 1113 | 1114 | - Name your functions. This is helpful for stack traces. 1115 | 1116 | ```javascript 1117 | // bad 1118 | var log = function(msg) { 1119 | console.log(msg); 1120 | }; 1121 | 1122 | // good 1123 | var log = function log(msg) { 1124 | console.log(msg); 1125 | }; 1126 | ``` 1127 | 1128 | - **Note:** IE8 and below exhibit some quirks with named function expressions. See [http://kangax.github.io/nfe/](http://kangax.github.io/nfe/) for more info. 1129 | 1130 | **[⬆ back to top](#table-of-contents)** 1131 | 1132 | 1133 | ## Accessors 1134 | 1135 | - Accessor functions for properties are not required 1136 | - If you do make accessor functions use getVal() and setVal('hello') 1137 | 1138 | ```javascript 1139 | // bad 1140 | dragon.age(); 1141 | 1142 | // good 1143 | dragon.getAge(); 1144 | 1145 | // bad 1146 | dragon.age(25); 1147 | 1148 | // good 1149 | dragon.setAge(25); 1150 | ``` 1151 | 1152 | - If the property is a boolean, use isVal() or hasVal() 1153 | 1154 | ```javascript 1155 | // bad 1156 | if (!dragon.age()) { 1157 | return false; 1158 | } 1159 | 1160 | // good 1161 | if (!dragon.hasAge()) { 1162 | return false; 1163 | } 1164 | ``` 1165 | 1166 | - It's okay to create get() and set() functions, but be consistent. 1167 | 1168 | ```javascript 1169 | function Jedi(options) { 1170 | options || (options = {}); 1171 | var lightsaber = options.lightsaber || 'blue'; 1172 | this.set('lightsaber', lightsaber); 1173 | } 1174 | 1175 | Jedi.prototype.set = function(key, val) { 1176 | this[key] = val; 1177 | }; 1178 | 1179 | Jedi.prototype.get = function(key) { 1180 | return this[key]; 1181 | }; 1182 | ``` 1183 | 1184 | **[⬆ back to top](#table-of-contents)** 1185 | 1186 | 1187 | ## Constructors 1188 | 1189 | - Assign methods to the prototype object, instead of overwriting the prototype with a new object. Overwriting the prototype makes inheritance impossible: by resetting the prototype you'll overwrite the base! 1190 | 1191 | ```javascript 1192 | function Jedi() { 1193 | console.log('new jedi'); 1194 | } 1195 | 1196 | // bad 1197 | Jedi.prototype = { 1198 | fight: function fight() { 1199 | console.log('fighting'); 1200 | }, 1201 | 1202 | block: function block() { 1203 | console.log('blocking'); 1204 | } 1205 | }; 1206 | 1207 | // good 1208 | Jedi.prototype.fight = function fight() { 1209 | console.log('fighting'); 1210 | }; 1211 | 1212 | Jedi.prototype.block = function block() { 1213 | console.log('blocking'); 1214 | }; 1215 | ``` 1216 | 1217 | - Methods can return `this` to help with method chaining. 1218 | 1219 | ```javascript 1220 | // bad 1221 | Jedi.prototype.jump = function() { 1222 | this.jumping = true; 1223 | return true; 1224 | }; 1225 | 1226 | Jedi.prototype.setHeight = function(height) { 1227 | this.height = height; 1228 | }; 1229 | 1230 | var luke = new Jedi(); 1231 | luke.jump(); // => true 1232 | luke.setHeight(20); // => undefined 1233 | 1234 | // good 1235 | Jedi.prototype.jump = function() { 1236 | this.jumping = true; 1237 | return this; 1238 | }; 1239 | 1240 | Jedi.prototype.setHeight = function(height) { 1241 | this.height = height; 1242 | return this; 1243 | }; 1244 | 1245 | var luke = new Jedi(); 1246 | 1247 | luke.jump() 1248 | .setHeight(20); 1249 | ``` 1250 | 1251 | 1252 | - It's okay to write a custom toString() method, just make sure it works successfully and causes no side effects. 1253 | 1254 | ```javascript 1255 | function Jedi(options) { 1256 | options || (options = {}); 1257 | this.name = options.name || 'no name'; 1258 | } 1259 | 1260 | Jedi.prototype.getName = function getName() { 1261 | return this.name; 1262 | }; 1263 | 1264 | Jedi.prototype.toString = function toString() { 1265 | return 'Jedi - ' + this.getName(); 1266 | }; 1267 | ``` 1268 | 1269 | **[⬆ back to top](#table-of-contents)** 1270 | 1271 | 1272 | ## Events 1273 | 1274 | - When attaching data payloads to events (whether DOM events or something more proprietary like Backbone events), pass a hash instead of a raw value. This allows a subsequent contributor to add more data to the event payload without finding and updating every handler for the event. For example, instead of: 1275 | 1276 | ```js 1277 | // bad 1278 | $(this).trigger('listingUpdated', listing.id); 1279 | 1280 | ... 1281 | 1282 | $(this).on('listingUpdated', function(e, listingId) { 1283 | // do something with listingId 1284 | }); 1285 | ``` 1286 | 1287 | prefer: 1288 | 1289 | ```js 1290 | // good 1291 | $(this).trigger('listingUpdated', { listingId : listing.id }); 1292 | 1293 | ... 1294 | 1295 | $(this).on('listingUpdated', function(e, data) { 1296 | // do something with data.listingId 1297 | }); 1298 | ``` 1299 | 1300 | **[⬆ back to top](#table-of-contents)** 1301 | 1302 | 1303 | ## Modules 1304 | 1305 | - The module should start with a `!`. This ensures that if a malformed module forgets to include a final semicolon there aren't errors in production when the scripts get concatenated. [Explanation](https://github.com/airbnb/javascript/issues/44#issuecomment-13063933) 1306 | - The file should be named with camelCase, live in a folder with the same name, and match the name of the single export. 1307 | - Add a method called `noConflict()` that sets the exported module to the previous version and returns this one. 1308 | - Always declare `'use strict';` at the top of the module. 1309 | 1310 | ```javascript 1311 | // fancyInput/fancyInput.js 1312 | 1313 | !function(global) { 1314 | 'use strict'; 1315 | 1316 | var previousFancyInput = global.FancyInput; 1317 | 1318 | function FancyInput(options) { 1319 | this.options = options || {}; 1320 | } 1321 | 1322 | FancyInput.noConflict = function noConflict() { 1323 | global.FancyInput = previousFancyInput; 1324 | return FancyInput; 1325 | }; 1326 | 1327 | global.FancyInput = FancyInput; 1328 | }(this); 1329 | ``` 1330 | 1331 | **[⬆ back to top](#table-of-contents)** 1332 | 1333 | 1334 | ## jQuery 1335 | 1336 | - Prefix jQuery object variables with a `$`. 1337 | 1338 | ```javascript 1339 | // bad 1340 | var sidebar = $('.sidebar'); 1341 | 1342 | // good 1343 | var $sidebar = $('.sidebar'); 1344 | ``` 1345 | 1346 | - Cache jQuery lookups. 1347 | 1348 | ```javascript 1349 | // bad 1350 | function setSidebar() { 1351 | $('.sidebar').hide(); 1352 | 1353 | // ...stuff... 1354 | 1355 | $('.sidebar').css({ 1356 | 'background-color': 'pink' 1357 | }); 1358 | } 1359 | 1360 | // good 1361 | function setSidebar() { 1362 | var $sidebar = $('.sidebar'); 1363 | $sidebar.hide(); 1364 | 1365 | // ...stuff... 1366 | 1367 | $sidebar.css({ 1368 | 'background-color': 'pink' 1369 | }); 1370 | } 1371 | ``` 1372 | 1373 | - For DOM queries use Cascading `$('.sidebar ul')` or parent > child `$('.sidebar > ul')`. [jsPerf](http://jsperf.com/jquery-find-vs-context-sel/16) 1374 | - Use `find` with scoped jQuery object queries. 1375 | 1376 | ```javascript 1377 | // bad 1378 | $('ul', '.sidebar').hide(); 1379 | 1380 | // bad 1381 | $('.sidebar').find('ul').hide(); 1382 | 1383 | // good 1384 | $('.sidebar ul').hide(); 1385 | 1386 | // good 1387 | $('.sidebar > ul').hide(); 1388 | 1389 | // good 1390 | $sidebar.find('ul').hide(); 1391 | ``` 1392 | 1393 | **[⬆ back to top](#table-of-contents)** 1394 | 1395 | 1396 | ## ECMAScript 5 Compatibility 1397 | 1398 | - Refer to [Kangax](https://twitter.com/kangax/)'s ES5 [compatibility table](http://kangax.github.com/es5-compat-table/) 1399 | 1400 | **[⬆ back to top](#table-of-contents)** 1401 | 1402 | 1403 | ## Testing 1404 | 1405 | - **Yup.** 1406 | 1407 | ```javascript 1408 | function() { 1409 | return true; 1410 | } 1411 | ``` 1412 | 1413 | **[⬆ back to top](#table-of-contents)** 1414 | --------------------------------------------------------------------------------