76 |
77 |
78 |
--------------------------------------------------------------------------------
/test/TypeInferenceSpec.js:
--------------------------------------------------------------------------------
1 | describe('type inference', function(){
2 | var typeinference = require('../src/typeinference'),
3 | types = require('../src/types');
4 | lexer = require('../src/lexer');
5 | parser = require('../lib/parser');
6 |
7 | beforeEach(function() {
8 | this.addMatchers({
9 | toStringEqual: function(expected) {
10 | return expected == this.actual.toString();
11 | }
12 | });
13 | });
14 |
15 | function parseCode(s) {
16 | return parser.parse(lexer.tokenise(s)).body;
17 | }
18 |
19 | function typeOfCode(s) {
20 | return typeinference.typecheck(parseCode(s), {}, {});
21 | }
22 |
23 | describe('should type literal', function() {
24 | it('numbers', function(){
25 | expect(typeOfCode('-1')).toStringEqual('Number');
26 | expect(typeOfCode('-99999')).toStringEqual('Number');
27 | expect(typeOfCode('0')).toStringEqual('Number');
28 | expect(typeOfCode('100')).toStringEqual('Number');
29 | });
30 |
31 | it('strings', function(){
32 | expect(typeOfCode('"100"')).toStringEqual('String');
33 | expect(typeOfCode('""')).toStringEqual('String');
34 | expect(typeOfCode("'100'")).toStringEqual('String');
35 | expect(typeOfCode("''")).toStringEqual('String');
36 | });
37 |
38 | it('booleans', function(){
39 | expect(typeOfCode('false')).toStringEqual('Boolean');
40 | expect(typeOfCode('true')).toStringEqual('Boolean');
41 | });
42 |
43 | it('arrays of primitives', function(){
44 | expect(typeOfCode('[""]')).toStringEqual('[String]');
45 | expect(typeOfCode('[true, false]')).toStringEqual('[Boolean]');
46 | expect(typeOfCode('[1, 2, 3]')).toStringEqual('[Number]');
47 | });
48 |
49 | it('empty arrays as generic', function() {
50 | var type = typeOfCode('[]');
51 | expect(type instanceof types.ArrayType).toBe(true);
52 | expect(type.type instanceof types.Variable).toBe(true);
53 | });
54 |
55 | it('structures', function() {
56 | expect(typeOfCode('{}')).toStringEqual('{}');
57 | expect(typeOfCode('{a: 1}')).toStringEqual('{a: Number}');
58 | expect(typeOfCode('{a: 1, b: true}')).toStringEqual('{a: Number, b: Boolean}');
59 | expect(typeOfCode("{'a': 1}")).toStringEqual('{"a": Number}');
60 | expect(typeOfCode('{"a": 1, \'b\': true}')).toStringEqual('{"a": Number, "b": Boolean}');
61 | expect(typeOfCode("{4: '1'}")).toStringEqual("{4: String}");
62 | expect(typeOfCode("{4: {'1': 1}}")).toStringEqual('{4: {"1": Number}}');
63 | });
64 | });
65 |
66 | describe("shouldn't type literal", function() {
67 | it('heterogeneous arrays', function() {
68 | expect(function() {
69 | typeOfCode('[1, true]');
70 | }).toThrow(new Error("Type error on line 0: Number is not Boolean"));
71 | });
72 | });
73 | });
74 |
--------------------------------------------------------------------------------
/site/codemirror2/lib/util/simple-hint.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | CodeMirror.simpleHint = function(editor, getHints) {
3 | // We want a single cursor position.
4 | if (editor.somethingSelected()) return;
5 | var result = getHints(editor);
6 | if (!result || !result.list.length) return;
7 | var completions = result.list;
8 | function insert(str) {
9 | editor.replaceRange(str, result.from, result.to);
10 | }
11 | // When there is only one completion, use it directly.
12 | if (completions.length == 1) {insert(completions[0]); return true;}
13 |
14 | // Build the select widget
15 | var complete = document.createElement("div");
16 | complete.className = "CodeMirror-completions";
17 | var sel = complete.appendChild(document.createElement("select"));
18 | // Opera doesn't move the selection when pressing up/down in a
19 | // multi-select, but it does properly support the size property on
20 | // single-selects, so no multi-select is necessary.
21 | if (!window.opera) sel.multiple = true;
22 | for (var i = 0; i < completions.length; ++i) {
23 | var opt = sel.appendChild(document.createElement("option"));
24 | opt.appendChild(document.createTextNode(completions[i]));
25 | }
26 | sel.firstChild.selected = true;
27 | sel.size = Math.min(10, completions.length);
28 | var pos = editor.cursorCoords();
29 | complete.style.left = pos.x + "px";
30 | complete.style.top = pos.yBot + "px";
31 | document.body.appendChild(complete);
32 | // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.
33 | var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth);
34 | if(winW - pos.x < sel.clientWidth)
35 | complete.style.left = (pos.x - sel.clientWidth) + "px";
36 | // Hack to hide the scrollbar.
37 | if (completions.length <= 10)
38 | complete.style.width = (sel.clientWidth - 1) + "px";
39 |
40 | var done = false;
41 | function close() {
42 | if (done) return;
43 | done = true;
44 | complete.parentNode.removeChild(complete);
45 | }
46 | function pick() {
47 | insert(completions[sel.selectedIndex]);
48 | close();
49 | setTimeout(function(){editor.focus();}, 50);
50 | }
51 | CodeMirror.connect(sel, "blur", close);
52 | CodeMirror.connect(sel, "keydown", function(event) {
53 | var code = event.keyCode;
54 | // Enter
55 | if (code == 13) {CodeMirror.e_stop(event); pick();}
56 | // Escape
57 | else if (code == 27) {CodeMirror.e_stop(event); close(); editor.focus();}
58 | else if (code != 38 && code != 40) {
59 | close(); editor.focus();
60 | // Pass the event to the CodeMirror instance so that it can handle things like backspace properly.
61 | editor.triggerOnKeyDown(event);
62 | setTimeout(function(){CodeMirror.simpleHint(editor, getHints);}, 50);
63 | }
64 | });
65 | CodeMirror.connect(sel, "dblclick", pick);
66 |
67 | sel.focus();
68 | // Opera sometimes ignores focusing a freshly created node
69 | if (window.opera) setTimeout(function(){if (!done) sel.focus();}, 100);
70 | return true;
71 | };
72 | })();
73 |
--------------------------------------------------------------------------------
/test/TypeParserSpec.js:
--------------------------------------------------------------------------------
1 | describe('compiler', function(){
2 | var jison = require('jison'),
3 | typegrammar = require('../src/typegrammar'),
4 | typeinference = require('../src/typeinference'),
5 | types = require('../src/types'),
6 | lexer = require('../src/lexer'),
7 | nodes = require('../src/nodes').nodes;
8 |
9 | typegrammar.startSymbol = 'oneType';
10 | typegrammar.bnf.oneType = [['type EOF', 'return $1;']];
11 |
12 | typeparser = new jison.Parser(typegrammar, { debug: true, noDefaultResolve: true });
13 |
14 | typeparser.yy = nodes
15 |
16 | typeparser.lexer = {
17 | "lex": function() {
18 | var token = this.tokens[this.pos] ? this.tokens[this.pos++] : ['EOF'];
19 | if ( token[2] != this.yylineno ) {
20 | this.column = 0
21 | } else {
22 | this.column += token[1].length;
23 | }
24 |
25 | this.yytext = token[1];
26 | this.yylineno = token[2];
27 | return token[0];
28 | },
29 | "setInput": function(tokens) {
30 | this.tokens = tokens;
31 | this.pos = 0;
32 | this.column = 0;
33 | },
34 | "upcomingInput": function() {
35 | return "";
36 | },
37 | "showPosition": function() {
38 | return 'column: ' + this.column;
39 | }
40 | };
41 |
42 | function parsedType(s) {
43 | var tokens = lexer.tokenise(s);
44 | var v = typeparser.parse(tokens);
45 | return typeinference.nodeToType(v, {}, {});
46 | }
47 |
48 | function expectEqualTypes(subject, target) {
49 | // FIXME: Use intentional equality once it is implemented
50 | expect(subject.toString()).toEqual(target.toString());
51 | }
52 |
53 | it('should parse atomic types', function() {
54 | expectEqualTypes( parsedType('Number'), new types.NumberType() );
55 | expectEqualTypes( parsedType('Boolean'),new types.BooleanType() );
56 | expectEqualTypes( parsedType('String'), new types.StringType() );
57 | });
58 |
59 | it('should parse object types', function() {
60 | expectEqualTypes( parsedType('{}'), new types.ObjectType({}) );
61 | expectEqualTypes( parsedType('{foo:String}'), new types.ObjectType({foo: new types.StringType()}) );
62 |
63 | expectEqualTypes( parsedType('{foo:String, baz:Number}'),
64 | new types.ObjectType({
65 | foo: new types.StringType(),
66 | baz: new types.NumberType()
67 | }));
68 | // TODO: expectEqualTypes( parsedType('{\n\tfoo:String\n}'), new types.ObjectType({foo: new types.StringType()}) );
69 | });
70 |
71 | it('should parse function types', function() {
72 | expectEqualTypes( parsedType('Function(String, String)'),
73 | new types.FunctionType([new types.StringType(), new types.StringType()]) );
74 | });
75 |
76 | it('should parse array types', function() {
77 | expectEqualTypes( parsedType('[Number]'),
78 | new types.ArrayType(new types.NumberType()) );
79 | });
80 | });
81 |
--------------------------------------------------------------------------------
/site/codemirror2/lib/codemirror.css:
--------------------------------------------------------------------------------
1 | .CodeMirror {
2 | line-height: 1em;
3 | font-family: monospace;
4 | }
5 |
6 | .CodeMirror-scroll {
7 | overflow: auto;
8 | height: 300px;
9 | /* This is needed to prevent an IE[67] bug where the scrolled content
10 | is visible outside of the scrolling box. */
11 | position: relative;
12 | outline: none;
13 | }
14 |
15 | .CodeMirror-gutter {
16 | position: absolute; left: 0; top: 0;
17 | z-index: 10;
18 | background-color: #f7f7f7;
19 | border-right: 1px solid #eee;
20 | min-width: 2em;
21 | height: 100%;
22 | }
23 | .CodeMirror-gutter-text {
24 | color: #aaa;
25 | text-align: right;
26 | padding: .4em .2em .4em .4em;
27 | white-space: pre !important;
28 | }
29 | .CodeMirror-lines {
30 | padding: .4em;
31 | white-space: pre;
32 | }
33 |
34 | .CodeMirror pre {
35 | -moz-border-radius: 0;
36 | -webkit-border-radius: 0;
37 | -o-border-radius: 0;
38 | border-radius: 0;
39 | border-width: 0; margin: 0; padding: 0; background: transparent;
40 | font-family: inherit;
41 | font-size: inherit;
42 | padding: 0; margin: 0;
43 | white-space: pre;
44 | word-wrap: normal;
45 | }
46 |
47 | .CodeMirror-wrap pre {
48 | word-wrap: break-word;
49 | white-space: pre-wrap;
50 | }
51 | .CodeMirror-wrap .CodeMirror-scroll {
52 | overflow-x: hidden;
53 | }
54 |
55 | .CodeMirror textarea {
56 | outline: none !important;
57 | }
58 |
59 | .CodeMirror pre.CodeMirror-cursor {
60 | z-index: 10;
61 | position: absolute;
62 | visibility: hidden;
63 | border-left: 1px solid black;
64 | border-right:none;
65 | width:0;
66 | }
67 | .CodeMirror pre.CodeMirror-cursor.CodeMirror-overwrite {}
68 | .CodeMirror-focused pre.CodeMirror-cursor {
69 | visibility: visible;
70 | }
71 |
72 | div.CodeMirror-selected { background: #d9d9d9; }
73 | .CodeMirror-focused div.CodeMirror-selected { background: #d7d4f0; }
74 |
75 | .CodeMirror-searching {
76 | background: #ffa;
77 | background: rgba(255, 255, 0, .4);
78 | }
79 |
80 | /* Default theme */
81 |
82 | .cm-s-default span.cm-keyword {color: #708;}
83 | .cm-s-default span.cm-atom {color: #219;}
84 | .cm-s-default span.cm-number {color: #164;}
85 | .cm-s-default span.cm-def {color: #00f;}
86 | .cm-s-default span.cm-variable {color: black;}
87 | .cm-s-default span.cm-variable-2 {color: #05a;}
88 | .cm-s-default span.cm-variable-3 {color: #085;}
89 | .cm-s-default span.cm-property {color: black;}
90 | .cm-s-default span.cm-operator {color: black;}
91 | .cm-s-default span.cm-comment {color: #a50;}
92 | .cm-s-default span.cm-string {color: #a11;}
93 | .cm-s-default span.cm-string-2 {color: #f50;}
94 | .cm-s-default span.cm-meta {color: #555;}
95 | .cm-s-default span.cm-error {color: #f00;}
96 | .cm-s-default span.cm-qualifier {color: #555;}
97 | .cm-s-default span.cm-builtin {color: #30a;}
98 | .cm-s-default span.cm-bracket {color: #cc7;}
99 | .cm-s-default span.cm-tag {color: #170;}
100 | .cm-s-default span.cm-attribute {color: #00c;}
101 | .cm-s-default span.cm-header {color: #a0a;}
102 | .cm-s-default span.cm-quote {color: #090;}
103 | .cm-s-default span.cm-hr {color: #999;}
104 | .cm-s-default span.cm-link {color: #00c;}
105 |
106 | span.cm-header, span.cm-strong {font-weight: bold;}
107 | span.cm-em {font-style: italic;}
108 | span.cm-emstrong {font-style: italic; font-weight: bold;}
109 | span.cm-link {text-decoration: underline;}
110 |
111 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
112 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
113 |
--------------------------------------------------------------------------------
/src/typegrammar.js:
--------------------------------------------------------------------------------
1 | var bnf = {
2 | // For type annotations
3 | "type": [
4 | ["IDENTIFIER optTypeParamList", "$$ = new yy.TypeName($1, $2);"],
5 | ["FUNCTION ( optTypeFunctionArgList )", "$$ = new yy.TypeFunction($3);"],
6 | ["GENERIC", "$$ = new yy.Generic($1);"],
7 | ["[ type ]", "$$ = new yy.TypeArray($2);"],
8 | ["( typeList )", "$$ = new yy.TypeObject($2);"],
9 | ["{ optTypePairs }", "$$ = new yy.TypeObject($2);"]
10 | ],
11 | "typeList": [
12 | ["type", "$$ = [$1];"],
13 | ["typeList , type", "$$ = $1; $1.push($3);"]
14 | ],
15 | "optTypeParamList": [
16 | ["", "$$ = [];"],
17 | ["typeParamList", "$$ = $1;"]
18 | ],
19 | "typeParamList": [
20 | ["IDENTIFIER", "$$ = [new yy.TypeName($1, [])];"],
21 | ["GENERIC", "$$ = [new yy.Generic($1, [])];"],
22 | ["( type )", "$$ = [$2];"],
23 | ["typeParamList IDENTIFIER", "$$ = $1; $1.push(new yy.TypeName($2, []));"],
24 | ["typeParamList GENERIC", "$$ = $1; $1.push(new yy.Generic($2, []));"],
25 | ["typeParamList ( type )", "$$ = $1; $1.push($3);"]
26 | ],
27 | "optTypeFunctionArgList": [
28 | ["", "$$ = [];"],
29 | ["typeFunctionArgList", "$$ = $1;"]
30 | ],
31 | "typeFunctionArgList": [
32 | ["type", "$$ = [$1];"],
33 | ["typeFunctionArgList , type", "$$ = $1; $1.push($3);"]
34 | ],
35 | "optTypePairs": [
36 | ["", "$$ = {};"],
37 | ["keywordOrIdentifier : type", "$$ = {}; $$[$1] = $3;"],
38 | ["optTypePairs , keywordOrIdentifier : type", "$$ = $1; $1[$3] = $5;"]
39 | ],
40 | "dataParamList": [
41 | ["IDENTIFIER", "$$ = [new yy.Arg($1)];"],
42 | ["dataParamList IDENTIFIER", "$$ = $1; $1.push(new yy.Arg($2));"]
43 | ],
44 | "optDataParamList": [
45 | ["", "$$ = [];"],
46 | ["dataParamList", "$$ = $1;"]
47 | ],
48 | "keywordOrIdentifier": [
49 | ["THEN", "$$ = $1;"],
50 | ["ELSE", "$$ = $1;"],
51 | ["DATA", "$$ = $1;"],
52 | ["TYPE", "$$ = $1;"],
53 | ["MATCH", "$$ = $1;"],
54 | ["CASE", "$$ = $1;"],
55 | ["DO", "$$ = $1;"],
56 | ["RETURN", "$$ = $1;"],
57 | ["WITH", "$$ = $1;"],
58 | ["WHERE", "$$ = $1;"],
59 | ["IDENTIFIER", "$$ = $1;"]
60 | ]
61 | };
62 | exports.bnf = bnf;
63 |
64 | var grammar = {
65 | "startSymbol": "typefile",
66 |
67 | "bnf": {
68 | "typefile": [
69 | ["EOF", "return {};"],
70 | ["body EOF", "return $1;"]
71 | ],
72 | "body": [
73 | ["pair", "$$ = {types: {}, env: {}}; if($1.data) { $$.types[$1.name] = $1.params; } else { $$.env[$1.name] = $1.type; }"],
74 | ["body TERMINATOR pair", "$$ = $1; if($3.data) { $$.types[$3.name] = $3.params; } else { $$.env[$3.name] = $3.type; }"],
75 | ["body TERMINATOR", "$$ = $1;"]
76 | ],
77 |
78 | "pair": [
79 | ["IDENTIFIER : type", "$$ = {name: $1, type: $3, data: false};"],
80 | ["TYPE IDENTIFIER optDataParamList", "$$ = {name: $2, params: $3, data: true};"]
81 | ],
82 |
83 | "type": bnf.type,
84 | "typeList": bnf.typeList,
85 | "optTypeParamList": bnf.optTypeParamList,
86 | "typeParamList": bnf.typeParamList,
87 | "optTypeFunctionArgList": bnf.optTypeFunctionArgList,
88 | "typeFunctionArgList": bnf.typeFunctionArgList,
89 | "optTypePairs": bnf.optTypePairs,
90 | "dataParamList": bnf.dataParamList,
91 | "optDataParamList": bnf.optDataParamList,
92 | "keywordOrIdentifier": bnf.keywordOrIdentifier
93 | }
94 | };
95 | exports.grammar = grammar;
96 |
--------------------------------------------------------------------------------
/grunt.js:
--------------------------------------------------------------------------------
1 | module.exports = function(grunt) {
2 | grunt.initConfig({
3 | lint: {
4 | src: [
5 | './src/*.js'
6 | ]
7 | },
8 | jison: {
9 | './lib/typeparser.js': './src/typegrammar.js',
10 | './lib/parser.js': './src/grammar.js'
11 | },
12 | cjsify: {
13 | 'roy.js': {
14 | entry: 'src/compile.js',
15 | dir: __dirname,
16 | options: {
17 | 'export': 'roy',
18 | 'ignoreMissing': true,
19 | 'node': false
20 | }
21 | }
22 | },
23 | jasmine: {
24 | src: './test'
25 | },
26 | min: {
27 | 'roy-min.js': 'roy.js'
28 | },
29 | watch: {
30 | parsers: {
31 | files: './src/*grammar.js',
32 | tasks: 'jison'
33 | },
34 | jasmine: {
35 | files: ['./src/*.js', './test/*Spec.js'],
36 | tasks: 'jasmine'
37 | }
38 | },
39 | jshint: {
40 | options: {
41 | es3: true,
42 | indent: 4,
43 | noarg: true,
44 | node: true,
45 | trailing: true,
46 | undef: true,
47 | unused: true
48 | }
49 | }
50 | });
51 |
52 | grunt.registerMultiTask('jison', 'Parser generator by jison.', function() {
53 | var Parser = require('jison').Parser,
54 | grammar = require(this.data).grammar;
55 | parser = new Parser(grammar, {debug: grunt.option('debug')}),
56 | fs = require('fs');
57 |
58 | fs.writeFileSync(this.target, parser.generate());
59 | });
60 |
61 | grunt.registerMultiTask('cjsify', 'Bundling by commonjs-everywhere.', function() {
62 | var cjsify = require('commonjs-everywhere').cjsify,
63 | escodegen = require('escodegen'),
64 | target = this.target,
65 | ast = cjsify(this.data.entry, this.data.dir, this.data.options),
66 | output = escodegen.generate(ast);
67 |
68 | grunt.file.write(target, output);
69 | });
70 |
71 | // Watching the task doesn't work. Sadly jasmine-node
72 | // executeSpecsInFolder is not idempotent
73 | grunt.registerMultiTask('jasmine', 'Testing by jasmine.', function() {
74 | var path = require('path'),
75 | specDir = this.file.src,
76 | badCache = grunt.file.expand(specDir).concat([
77 | path.dirname(require.resolve("jasmine-node")),
78 | ]),
79 | jasmine,
80 | done = this.async(),
81 | key;
82 |
83 | // Would be nice to use grunt.file.clearRequireCache
84 | grunt.utils._.each(require.cache, function(v, k) {
85 | var isBad = grunt.utils._.any(badCache, function(dir) {
86 | return k.indexOf(path.resolve(dir)) === 0;
87 | });
88 | if(!isBad) return;
89 | delete require.cache[k];
90 | });
91 |
92 | jasmine = require("jasmine-node");
93 |
94 | // Not nice (necessary for jasmine-node's asyncSpecWait, etc)
95 | for(key in jasmine) if(jasmine[key] instanceof Function) global[key] = jasmine[key];
96 |
97 | function onComplete(runner) {
98 | if (runner.results().failedCount > 0) {
99 | process.exit(1);
100 | return;
101 | }
102 | done(true);
103 | };
104 |
105 | jasmine.executeSpecsInFolder({
106 | 'specFolders': [specDir],
107 | 'onComplete': onComplete,
108 | 'isVerbose': false,
109 | 'showColors': true
110 | });
111 | });
112 |
113 | grunt.registerTask('default', 'jison lint jasmine cjsify min');
114 | };
115 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## Roy Change Log
2 |
3 | ### 0.2.2
4 | ##### 2013-06-29
5 |
6 | * Fix #182: TypeError: Cannot set property 'leadingComments' of undefined
7 |
8 | ### 0.2.1
9 | ##### 2013-06-29
10 |
11 | * Fix #180: Support for scientific notated numbers?
12 | * Fix evaluation of Roy code on website.
13 | * Move jison from dependencies to devDependencies.
14 |
15 | ### 0.2.0
16 | ##### 2013-06-29
17 |
18 | * **BREAKING CHANGE** Removed metaprogramming.
19 | * **BREAKING CHANGE** Changed array accessor from `!` to `@`.
20 |
21 | * Compiler rewritten as proper AST using escodegen.
22 | * New test suite.
23 | * Replaced interleaved with rigger.
24 | * Started using codemirror on the site.
25 |
26 | * Added boolean not `!`.
27 | * Added CompileSpec, TarjanSpec and TypeInferenceSpec to test suite.
28 | * Added dependencies (escodegen, source-map).
29 | * Added devDependencies (grunt, jasmine-node, rigger).
30 | * Added documentation for array access.
31 | * Added grunt support.
32 | * Added module (vim-roy).
33 | * Added scientific notation support.
34 | * Added shebang.
35 | * Added strings as keys for objects.
36 | * Added tests (match_expression_single_eval, monoid, object).
37 | * Added travis support.
38 | * Added type classes.
39 |
40 | * Removed devDependencies (interleave).
41 | * Removed example (macro).
42 | * Removed meta-programming example from site.
43 |
44 | * Updated dependencies (jison, unicode-categories).
45 | * Updated examples (defferedmonad, option).
46 | * Updated miscellaneous (chrome-extension, roy-mode, roy.tmbundle).
47 | * Updated prelude (nil, leftMonad, rightMonad).
48 |
49 | ### 0.1.5
50 | ##### 2012-03-19
51 |
52 | * Fixed typo in introduction documentation.
53 |
54 | * Added examples (module, node_module).
55 | * Added files (module, macroexpand, typegrammar).
56 | * Added module example to site.
57 | * Added strings, object type example, native types and regex to types documentation.
58 | * Added tests (accessors, coercing_native_to_any, conditionals, where).
59 |
60 | * Updated examples (data, option, structural).
61 | * Updated grammar (generic, function types, where).
62 | * Updated prelude (π).
63 | * Updated tests (deep_matching, tagged_unions).
64 |
65 | ### 0.1.4
66 | ##### 2012-01-23
67 | * **BREAKING CHANGE** lambda syntax `\[args] -> ` instead of `fn [args] = `.
68 | * **BREAKING CHANGE** do syntax ` <- ` instead of `bind = `.
69 |
70 | * New features
71 | * Optional type parameter lists for arguments.
72 |
73 | * Color console for repl.
74 | * Conversion from mercurial to git.
75 | * Converted examples to new lambda style (funcs, option, structural).
76 | * Converted tests to new syntax (functions, trace_monad).
77 | * Help in repl and binary.
78 | * Makefile supports extensions.
79 | * Renamed stdlib to prelude and extended.
80 | * Started documentation (index, introduction, types).
81 |
82 | * Added ajax and deferred examples to site.
83 | * Added chrome extension.
84 | * Added dependencies (unicode-categories).
85 | * Added examples (ajaxmonad, deferredmonad, fizzbuzz, sqrt, tracemonad).
86 | * Added MIT license.
87 | * Added tests (map, option_monad, unicode).
88 |
89 | * Updated devDependencies (interleave).
90 | * Updated examples (data, types).
91 | * Updated roy-mode and roy.tmbundle extensions.
92 |
93 | ### 0.1.3
94 | ##### 2011-11-19
95 |
96 | * Specific `jison` version.
97 | * Fixed fall-through bug in repl.
98 |
99 | ### 0.1.2
100 | ##### 2011-11-18
101 |
102 | * New features
103 | * Compile-time meta-programming.
104 | * Simple tagged unions.
105 | * Pattern matching.
106 | * Structural typing.
107 | * Monad syntax.
108 | * Not-horrible JS output.
109 |
110 | * Move from toy language to full fledged language.
111 |
112 | * Added examples (alias, data, macro, option, stdlib, structural).
113 | * Added stdlib.
114 | * Added website.
115 | * Added test suite (deep_matching, functions, primitive_types, tagged_unions, trace_monad).
116 |
117 | * Updated examples (funcs, types).
118 |
--------------------------------------------------------------------------------
/docs/guide/introduction.rst:
--------------------------------------------------------------------------------
1 | Introduction
2 | ============
3 |
4 | Roy is a programming language that targets JavaScript. It has a few
5 | main features:
6 |
7 | * Damas-Hindley-Milner type inference
8 | * Whitespace significant syntax
9 | * Simple tagged unions
10 | * Pattern matching
11 | * Structural typing
12 | * Monad syntax
13 | * Not-horrible JS output
14 |
15 | Most of these features are common in statically-typed, functional
16 | languages, such as:
17 |
18 | * Haskell_
19 | * OCaml_
20 | * Scala_
21 |
22 | Why JavaScript?
23 | ---------------
24 |
25 | JavaScript is a necessity for web applications. It is the only
26 | feasible language you can natively run in your web browser.
27 |
28 | A lot of developers are now using JavaScript outside of the browser;
29 | you can use `node.js`_ and Rhino_ to write server-side JavaScript.
30 |
31 | What not just write JavaScript?
32 | -------------------------------
33 |
34 | As universal as JavaScript has become, the language itself has
35 | problems:
36 |
37 | Boilerplate
38 | ***********
39 |
40 | Creating functions is something that functional programmers want to do
41 | but JavaScript makes this a bit too verbose:
42 |
43 | .. code-block:: javascript
44 |
45 | var x = function(a, b, c){
46 | return a + b + c;
47 | };
48 |
49 | One solution would be to come up with a shorthand syntax for functions
50 | and have implicit returns. We might also be able to get rid of those
51 | braces.
52 |
53 | Tolerance
54 | *********
55 |
56 | JavaScript tries to guess what the developer is trying to do:
57 |
58 | .. code-block:: javascript
59 |
60 | var two = 1 + true;
61 | console.log(two == "2"); // true
62 |
63 | It doesn't really make sense to add the number ``1`` to the Boolean
64 | ``true`` value. It doesn't really make sense to compare the number
65 | ``2`` to the string ``"2"``.
66 |
67 | For correctness, we should show an error instead of trying to guess
68 | what the programmer wants.
69 |
70 | Complexity
71 | **********
72 |
73 | JavaScript allows multiple ways to do the same thing. Which is the
74 | correct way to construct a Person?
75 |
76 | .. code-block:: javascript
77 |
78 | var p1 = Person();
79 | var p2 = new Person();
80 |
81 | It depends on the library and can be a source of confusion.
82 |
83 | Dangerous
84 | *********
85 |
86 | It can be easy to do things with unintentional
87 | side-effects. JavaScript has the ``var`` keyword to create local
88 | variables but unqualified assignments write to the global object:
89 |
90 | .. code-block:: javascript
91 |
92 | var x = 10;
93 |
94 | function getX() {
95 | x = 100; // forgot 'var'
96 | return x;
97 | }
98 |
99 | console.log(getX()); // 100
100 | console.log(x); // 100
101 |
102 | What can we do?
103 | ---------------
104 |
105 | We've identified some problems with JavaScript, what can we do to fix
106 | it?
107 |
108 | * Try to replace JavaScript in the browser with another language
109 | * Try to replace JavaScript in the browser with a general purpose
110 | bytecode
111 | * Change the JavaScript standard
112 | * Compile from another language to JavaScript
113 |
114 | The last option is the path of least resistance. In fact, there's
115 | already `quite a few languages`_ that compile to JavaScript, the most
116 | popular being CoffeeScript_, haXe_ and Objective-J_.
117 |
118 | There also ways to compile Haskell, OCaml and Scala to
119 | JavaScript. These can help with writing statically-typed, functional
120 | code for the browser but they usually have a few downsides:
121 |
122 | * Hard/impossible to interoperate with JavaScript libraries
123 | * Generate a lot of code
124 | * Generate code that requires a hefty runtime
125 | * Must be compiled on the server-side (not in the browser)
126 |
127 | The Roy solution
128 | ----------------
129 |
130 | After trying to write correct programs in JavaScript and languages
131 | that compile to JavaScript, Roy was created. Roy tries to keep close
132 | to JavaScript semantics for ease of interoperability and code
133 | generation. It's also written in JavaScript so that it can compile
134 | code from the browser.
135 |
136 | One of the biggest ideas when coming from JavaScript is the use of
137 | compile-time type-checking to remove type errors. We'll cover that in
138 | the next chapter.
139 |
140 | .. _node.js: http://nodejs.org/
141 | .. _Rhino: http://www.mozilla.org/rhino/
142 | .. _Haskell: http://haskell.org/
143 | .. _OCaml: http://caml.inria.fr/
144 | .. _Scala: http://scala-lang.org/
145 | .. _quite a few languages: http://altjs.org/
146 | .. _CoffeeScript: http://coffeescript.org/
147 | .. _haXe: http://haxe.org/
148 | .. _Objective-J: http://cappuccino.org/
149 |
--------------------------------------------------------------------------------
/test/CompileSpec.js:
--------------------------------------------------------------------------------
1 | describe('compiler', function(){
2 | var roy = require('../src/compile'),
3 | fs = require('fs'),
4 | path = require('path'),
5 | child_process = require('child_process'),
6 | processBin = process.argv[0];
7 |
8 | function compilerOutput(s) {
9 | return roy.compile(s, {}, {}, {nodejs: true}).output;
10 | }
11 |
12 | function fixtureCompilerOutput(s) {
13 | return compilerOutput(fs.readFileSync(path.join('test', 'fixtures', s + '.roy'), 'utf8'));
14 | }
15 |
16 | function fixtureExpectedOutput(s) {
17 | return fs.readFileSync(path.join('test', 'fixtures', s + '.out'), 'utf8');
18 | }
19 |
20 | function expectExecutionToHaveExpectedOutput(s) {
21 | var expected = fixtureExpectedOutput(s);
22 | var compiled = fixtureCompilerOutput(s);
23 |
24 | var child = child_process.spawn(processBin);
25 | child.stdin.write(compiled, 'utf8');
26 | child.stdin.end();
27 | child.stdout.setEncoding('utf8');
28 |
29 | var actual = '';
30 | asyncSpecWait();
31 | child.stdout.on('data', function(d) {
32 | actual += d;
33 | });
34 | child.stdout.on('end', function() {
35 | expect(actual).toEqual(expected);
36 | asyncSpecDone();
37 | });
38 | }
39 |
40 | it('should preserve comments', function(){
41 | expect(compilerOutput('// HELLO\nconsole.log 123')).toEqual('// HELLO\nconsole.log(123);');
42 | });
43 | it('should preserve comments on non output nodes', function(){
44 | expect(compilerOutput('// Comment\ntype X = {a: Number}\nlet x:X = {a: 123}'))
45 | .toEqual('// Comment\nvar x = { \'a\': 123 };');
46 | });
47 |
48 | it('should compile literal', function(){
49 | expect(compilerOutput('42')).toEqual('42;');
50 | expect(compilerOutput('\'string\'')).toEqual('\'string\';');
51 | expect(compilerOutput('null')).toEqual('null;');
52 | });
53 |
54 | it('should compile identifier', function(){
55 | expect(compilerOutput('roy')).toEqual('roy;');
56 | });
57 |
58 | it('should only execute a match expression once', function(){
59 | expectExecutionToHaveExpectedOutput('good/match_expression_single_eval');
60 | });
61 |
62 | describe('should execute', function() {
63 | it('accessors.roy with expected output', function() {
64 | expectExecutionToHaveExpectedOutput('good/accessors');
65 | });
66 | it('coercing_native_to_any.roy with expected output', function() {
67 | expectExecutionToHaveExpectedOutput('good/coercing_native_to_any');
68 | });
69 | it('conditionals.roy with expected output', function() {
70 | expectExecutionToHaveExpectedOutput('good/conditionals');
71 | });
72 | it('deep_matching.roy with expected output', function() {
73 | expectExecutionToHaveExpectedOutput('good/deep_matching');
74 | });
75 | it('functions.roy with expected output', function() {
76 | expectExecutionToHaveExpectedOutput('good/functions');
77 | });
78 | it('map.roy with expected output', function() {
79 | expectExecutionToHaveExpectedOutput('good/map');
80 | });
81 | it('monoid.roy with expected output', function() {
82 | expectExecutionToHaveExpectedOutput('good/monoid');
83 | });
84 | it('number.roy with expected output', function() {
85 | expectExecutionToHaveExpectedOutput('good/number');
86 | });
87 | it('object.roy with expected output', function() {
88 | expectExecutionToHaveExpectedOutput('good/object');
89 | });
90 | it('option_monad.roy with expected output', function() {
91 | expectExecutionToHaveExpectedOutput('good/option_monad');
92 | });
93 | it('primitive_types.roy with expected output', function() {
94 | expectExecutionToHaveExpectedOutput('good/primitive_types');
95 | });
96 | it('tagged_unions.roy with expected output', function() {
97 | expectExecutionToHaveExpectedOutput('good/tagged_unions');
98 | });
99 | it('trace_monad.roy with expected output', function() {
100 | expectExecutionToHaveExpectedOutput('good/trace_monad');
101 | });
102 | it('unicode.roy with expected output', function() {
103 | expectExecutionToHaveExpectedOutput('good/unicode');
104 | });
105 | it('where.roy with expected output', function() {
106 | expectExecutionToHaveExpectedOutput('good/where');
107 | });
108 | it('with.roy with expected output', function() {
109 | expectExecutionToHaveExpectedOutput('good/with');
110 | });
111 | });
112 | });
113 |
--------------------------------------------------------------------------------
/docs/guide/types.rst:
--------------------------------------------------------------------------------
1 | Types
2 | =====
3 |
4 | The `Curry-Howard isomorphism`_ states that types are theorems and
5 | programs are proofs. Roy takes the stance of not generating programs
6 | (proofs) if the types (theorems) don't make sense.
7 |
8 | This is called static type-checking and is a useful tool for writing
9 | correct programs.
10 |
11 | Primitives
12 | ----------
13 |
14 | Roy implements the same primitive types as JavaScript:
15 |
16 | * Number
17 | * Boolean
18 | * String
19 | * Array
20 | * Object
21 |
22 | In Roy, Arrays and Objects have special semantics and are composed of
23 | multiple types. We'll separately cover typing of :ref:`arrays` and
24 | typing of :ref:`objects`.
25 |
26 | The Read Evaluate Print Loop
27 | ----------------------------
28 |
29 | Roy has an interactive mode which allows compilation and execution of
30 | code.
31 |
32 | The simplest example is a value. Putting in the number ``1`` will
33 | respond back with that same value and its type::
34 |
35 | roy> 1
36 | 1 : Number
37 |
38 | We could also give a string of characters::
39 |
40 | roy> "Hello world!"
41 | Hello world! : String
42 |
43 | Or a Boolean::
44 |
45 | roy> true
46 | true : Boolean
47 |
48 | Evaluating expressions will also tell you the type of the result::
49 |
50 | roy> 1 + 1
51 | 2 : Number
52 |
53 | Let's make the type-checking fail::
54 |
55 | roy> 1 + "Test"
56 | Error: Type error: Number is not String
57 |
58 | Notice that it doesn't give you an answer to the
59 | expression. JavaScript at this point would instead guess what you
60 | meant and give you an answer of ``"1Test"``.
61 |
62 | .. _strings:
63 |
64 | Strings
65 | -------
66 |
67 | These behave similar to a Javascript String, but they won't have methods attached to them.
68 | String concatentation is done with the '++' operator.
69 |
70 | .. _arrays:
71 |
72 | Arrays
73 | ------
74 |
75 | Arrays are homogeneous, meaning that they can only hold elements of
76 | the same type. For example, we can make an Array of Numbers::
77 |
78 | roy> [1, 2, 3]
79 | 1,2,3 : [Number]
80 |
81 | Trying to treat it as a heterogeneous collection will result in a type
82 | error::
83 |
84 | roy> [1, true, 3]
85 | Error: Type error: Number is not Boolean
86 |
87 | Access array elements with the ``@`` operator::
88 |
89 | roy> [1,2,3] @ 1
90 | 2 : Number
91 |
92 | .. _objects:
93 |
94 | Objects
95 | -------
96 |
97 | Objects use `structural subtyping`_. An object is a "subtype" of
98 | another object if it satisfies all of the properties that the other
99 | object has.
100 |
101 | An empty object is the supertype of all objects::
102 |
103 | roy> {}
104 | [object Object] : {}
105 |
106 | An object containing a single property is a subtype of only the empty
107 | object::
108 |
109 | roy> {a: 100}
110 | [object Object] : {a: Number}
111 |
112 | This property can be used to write well-typed code that works on object properties::
113 |
114 | roy> let a = {a:100}
115 | roy> let b = {a:5, b:5}
116 | roy> let f o = o.a + 6
117 | roy> f a
118 | 106 : Number
119 | roy> f b
120 | 11 : Number
121 | roy> let d = {b:100}
122 | roy> f d
123 | Error: Type error: {b: Number} is not {a: Number}
124 |
125 | Interoperating with JavaScript
126 | ------------------------------
127 |
128 | Referring to unknown identifier will assume that the identifier refers
129 | to a native JavaScript global.
130 |
131 | For example, you can refer to ``console.log``, something not known
132 | natively to Roy::
133 |
134 | roy> console.log "Hello!"
135 | Hello!
136 |
137 |
138 | Using Native types
139 | --------------------
140 |
141 | Given Roy's current limitations, you may want to use a Native type sometimes::
142 |
143 | roy> "abc".length
144 | Error: Parse error on line 2: Unexpected '.'
145 |
146 | roy> (String "abc")
147 | abc : Native
148 | roy> (String "abc").length
149 | 3 : Native
150 |
151 | Regular Expressions
152 | ------------------------------
153 |
154 | Roy does not have direct support for regular expressions, including literals like /exp/
155 |
156 | To use a regular expression in Roy you need one of the following approaches:
157 |
158 | * Have an existing RegExp
159 | * Create a native RegExp using the RegExp constructor
160 | * Invoke match on a Native String, which converts the matching String to a RegExp
161 |
162 | ::
163 |
164 | roy> (String "abcd").match "a.c"
165 | ["abc"] : Native
166 |
167 | roy> (RegExp("a.c")).exec 'abcd'
168 | ["abc"] : Native
169 |
170 | If you want, you can try and shorten up RegExp construction::
171 |
172 | roy> let r s = RegExp s
173 | roy> r "a.c"
174 | /a.c/ : Native
175 | roy> r"a.c"
176 | /a.c/ : Native
177 |
178 | roy> (r"a.c").exec "abcd"
179 | ["abc"] : Native
180 |
181 | .. _Curry-Howard isomorphism: http://en.wikipedia.org/wiki/Curry-Howard_correspondence
182 | .. _structural subtyping: http://en.wikipedia.org/wiki/Structural_type_system
183 | Access array elements with the @ operator
184 |
--------------------------------------------------------------------------------
/site/codemirror2/lib/util/search.js:
--------------------------------------------------------------------------------
1 | // Define search commands. Depends on dialog.js or another
2 | // implementation of the openDialog method.
3 |
4 | // Replace works a little oddly -- it will do the replace on the next
5 | // Ctrl-G (or whatever is bound to findNext) press. You prevent a
6 | // replace by making sure the match is no longer selected when hitting
7 | // Ctrl-G.
8 |
9 | (function() {
10 | function SearchState() {
11 | this.posFrom = this.posTo = this.query = null;
12 | this.marked = [];
13 | }
14 | function getSearchState(cm) {
15 | return cm._searchState || (cm._searchState = new SearchState());
16 | }
17 | function dialog(cm, text, shortText, f) {
18 | if (cm.openDialog) cm.openDialog(text, f);
19 | else f(prompt(shortText, ""));
20 | }
21 | function confirmDialog(cm, text, shortText, fs) {
22 | if (cm.openConfirm) cm.openConfirm(text, fs);
23 | else if (confirm(shortText)) fs[0]();
24 | }
25 | function parseQuery(query) {
26 | var isRE = query.match(/^\/(.*)\/$/);
27 | return isRE ? new RegExp(isRE[1]) : query;
28 | }
29 | var queryDialog =
30 | 'Search: (Use /re/ syntax for regexp search)';
31 | function doSearch(cm, rev) {
32 | var state = getSearchState(cm);
33 | if (state.query) return findNext(cm, rev);
34 | dialog(cm, queryDialog, "Search for:", function(query) {
35 | cm.operation(function() {
36 | if (!query || state.query) return;
37 | state.query = parseQuery(query);
38 | if (cm.lineCount() < 2000) { // This is too expensive on big documents.
39 | for (var cursor = cm.getSearchCursor(query); cursor.findNext();)
40 | state.marked.push(cm.markText(cursor.from(), cursor.to(), "CodeMirror-searching"));
41 | }
42 | state.posFrom = state.posTo = cm.getCursor();
43 | findNext(cm, rev);
44 | });
45 | });
46 | }
47 | function findNext(cm, rev) {cm.operation(function() {
48 | var state = getSearchState(cm);
49 | var cursor = cm.getSearchCursor(state.query, rev ? state.posFrom : state.posTo);
50 | if (!cursor.find(rev)) {
51 | cursor = cm.getSearchCursor(state.query, rev ? {line: cm.lineCount() - 1} : {line: 0, ch: 0});
52 | if (!cursor.find(rev)) return;
53 | }
54 | cm.setSelection(cursor.from(), cursor.to());
55 | state.posFrom = cursor.from(); state.posTo = cursor.to();
56 | })}
57 | function clearSearch(cm) {cm.operation(function() {
58 | var state = getSearchState(cm);
59 | if (!state.query) return;
60 | state.query = null;
61 | for (var i = 0; i < state.marked.length; ++i) state.marked[i].clear();
62 | state.marked.length = 0;
63 | })}
64 |
65 | var replaceQueryDialog =
66 | 'Replace: (Use /re/ syntax for regexp search)';
67 | var replacementQueryDialog = 'With: ';
68 | var doReplaceConfirm = "Replace? ";
69 | function replace(cm, all) {
70 | dialog(cm, replaceQueryDialog, "Replace:", function(query) {
71 | if (!query) return;
72 | query = parseQuery(query);
73 | dialog(cm, replacementQueryDialog, "Replace with:", function(text) {
74 | if (all) {
75 | cm.operation(function() {
76 | for (var cursor = cm.getSearchCursor(query); cursor.findNext();) {
77 | if (typeof query != "string") {
78 | var match = cm.getRange(cursor.from(), cursor.to()).match(query);
79 | cursor.replace(text.replace(/\$(\d)/, function(w, i) {return match[i];}));
80 | } else cursor.replace(text);
81 | }
82 | });
83 | } else {
84 | clearSearch(cm);
85 | var cursor = cm.getSearchCursor(query, cm.getCursor());
86 | function advance() {
87 | var start = cursor.from(), match;
88 | if (!(match = cursor.findNext())) {
89 | cursor = cm.getSearchCursor(query);
90 | if (!(match = cursor.findNext()) ||
91 | (cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
92 | }
93 | cm.setSelection(cursor.from(), cursor.to());
94 | confirmDialog(cm, doReplaceConfirm, "Replace?",
95 | [function() {doReplace(match);}, advance]);
96 | }
97 | function doReplace(match) {
98 | cursor.replace(typeof query == "string" ? text :
99 | text.replace(/\$(\d)/, function(w, i) {return match[i];}));
100 | advance();
101 | }
102 | advance();
103 | }
104 | });
105 | });
106 | }
107 |
108 | CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);};
109 | CodeMirror.commands.findNext = doSearch;
110 | CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);};
111 | CodeMirror.commands.clearSearch = clearSearch;
112 | CodeMirror.commands.replace = replace;
113 | CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);};
114 | })();
115 |
--------------------------------------------------------------------------------
/site/codemirror2/lib/util/searchcursor.js:
--------------------------------------------------------------------------------
1 | (function(){
2 | function SearchCursor(cm, query, pos, caseFold) {
3 | this.atOccurrence = false; this.cm = cm;
4 | if (caseFold == null) caseFold = typeof query == "string" && query == query.toLowerCase();
5 |
6 | pos = pos ? cm.clipPos(pos) : {line: 0, ch: 0};
7 | this.pos = {from: pos, to: pos};
8 |
9 | // The matches method is filled in based on the type of query.
10 | // It takes a position and a direction, and returns an object
11 | // describing the next occurrence of the query, or null if no
12 | // more matches were found.
13 | if (typeof query != "string") // Regexp match
14 | this.matches = function(reverse, pos) {
15 | if (reverse) {
16 | var line = cm.getLine(pos.line).slice(0, pos.ch), match = line.match(query), start = 0;
17 | while (match) {
18 | var ind = line.indexOf(match[0]);
19 | start += ind;
20 | line = line.slice(ind + 1);
21 | var newmatch = line.match(query);
22 | if (newmatch) match = newmatch;
23 | else break;
24 | start++;
25 | }
26 | }
27 | else {
28 | var line = cm.getLine(pos.line).slice(pos.ch), match = line.match(query),
29 | start = match && pos.ch + line.indexOf(match[0]);
30 | }
31 | if (match)
32 | return {from: {line: pos.line, ch: start},
33 | to: {line: pos.line, ch: start + match[0].length},
34 | match: match};
35 | };
36 | else { // String query
37 | if (caseFold) query = query.toLowerCase();
38 | var fold = caseFold ? function(str){return str.toLowerCase();} : function(str){return str;};
39 | var target = query.split("\n");
40 | // Different methods for single-line and multi-line queries
41 | if (target.length == 1)
42 | this.matches = function(reverse, pos) {
43 | var line = fold(cm.getLine(pos.line)), len = query.length, match;
44 | if (reverse ? (pos.ch >= len && (match = line.lastIndexOf(query, pos.ch - len)) != -1)
45 | : (match = line.indexOf(query, pos.ch)) != -1)
46 | return {from: {line: pos.line, ch: match},
47 | to: {line: pos.line, ch: match + len}};
48 | };
49 | else
50 | this.matches = function(reverse, pos) {
51 | var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(cm.getLine(ln));
52 | var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match));
53 | if (reverse ? offsetA >= pos.ch || offsetA != match.length
54 | : offsetA <= pos.ch || offsetA != line.length - match.length)
55 | return;
56 | for (;;) {
57 | if (reverse ? !ln : ln == cm.lineCount() - 1) return;
58 | line = fold(cm.getLine(ln += reverse ? -1 : 1));
59 | match = target[reverse ? --idx : ++idx];
60 | if (idx > 0 && idx < target.length - 1) {
61 | if (line != match) return;
62 | else continue;
63 | }
64 | var offsetB = (reverse ? line.lastIndexOf(match) : line.indexOf(match) + match.length);
65 | if (reverse ? offsetB != line.length - match.length : offsetB != match.length)
66 | return;
67 | var start = {line: pos.line, ch: offsetA}, end = {line: ln, ch: offsetB};
68 | return {from: reverse ? end : start, to: reverse ? start : end};
69 | }
70 | };
71 | }
72 | }
73 |
74 | SearchCursor.prototype = {
75 | findNext: function() {return this.find(false);},
76 | findPrevious: function() {return this.find(true);},
77 |
78 | find: function(reverse) {
79 | var self = this, pos = this.cm.clipPos(reverse ? this.pos.from : this.pos.to);
80 | function savePosAndFail(line) {
81 | var pos = {line: line, ch: 0};
82 | self.pos = {from: pos, to: pos};
83 | self.atOccurrence = false;
84 | return false;
85 | }
86 |
87 | for (;;) {
88 | if (this.pos = this.matches(reverse, pos)) {
89 | this.atOccurrence = true;
90 | return this.pos.match || true;
91 | }
92 | if (reverse) {
93 | if (!pos.line) return savePosAndFail(0);
94 | pos = {line: pos.line-1, ch: this.cm.getLine(pos.line-1).length};
95 | }
96 | else {
97 | var maxLine = this.cm.lineCount();
98 | if (pos.line == maxLine - 1) return savePosAndFail(maxLine);
99 | pos = {line: pos.line+1, ch: 0};
100 | }
101 | }
102 | },
103 |
104 | from: function() {if (this.atOccurrence) return this.pos.from;},
105 | to: function() {if (this.atOccurrence) return this.pos.to;},
106 |
107 | replace: function(newText) {
108 | var self = this;
109 | if (this.atOccurrence)
110 | self.pos.to = this.cm.replaceRange(newText, self.pos.from, self.pos.to);
111 | }
112 | };
113 |
114 | CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) {
115 | return new SearchCursor(this, query, pos, caseFold);
116 | });
117 | })();
118 |
--------------------------------------------------------------------------------
/site/codemirror2/lib/util/javascript-hint.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | function forEach(arr, f) {
3 | for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);
4 | }
5 |
6 | function arrayContains(arr, item) {
7 | if (!Array.prototype.indexOf) {
8 | var i = arr.length;
9 | while (i--) {
10 | if (arr[i] === item) {
11 | return true;
12 | }
13 | }
14 | return false;
15 | }
16 | return arr.indexOf(item) != -1;
17 | }
18 |
19 | function scriptHint(editor, keywords, getToken) {
20 | // Find the token at the cursor
21 | var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token;
22 | // If it's not a 'word-style' token, ignore the token.
23 | if (!/^[\w$_]*$/.test(token.string)) {
24 | token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state,
25 | className: token.string == "." ? "property" : null};
26 | }
27 | // If it is a property, find out what it is a property of.
28 | while (tprop.className == "property") {
29 | tprop = getToken(editor, {line: cur.line, ch: tprop.start});
30 | if (tprop.string != ".") return;
31 | tprop = getToken(editor, {line: cur.line, ch: tprop.start});
32 | if (tprop.string == ')') {
33 | var level = 1;
34 | do {
35 | tprop = getToken(editor, {line: cur.line, ch: tprop.start});
36 | switch (tprop.string) {
37 | case ')': level++; break;
38 | case '(': level--; break;
39 | default: break;
40 | }
41 | } while (level > 0)
42 | tprop = getToken(editor, {line: cur.line, ch: tprop.start});
43 | if (tprop.className == 'variable')
44 | tprop.className = 'function';
45 | else return; // no clue
46 | }
47 | if (!context) var context = [];
48 | context.push(tprop);
49 | }
50 | return {list: getCompletions(token, context, keywords),
51 | from: {line: cur.line, ch: token.start},
52 | to: {line: cur.line, ch: token.end}};
53 | }
54 |
55 | CodeMirror.javascriptHint = function(editor) {
56 | return scriptHint(editor, javascriptKeywords,
57 | function (e, cur) {return e.getTokenAt(cur);});
58 | }
59 |
60 | function getCoffeeScriptToken(editor, cur) {
61 | // This getToken, it is for coffeescript, imitates the behavior of
62 | // getTokenAt method in javascript.js, that is, returning "property"
63 | // type and treat "." as indepenent token.
64 | var token = editor.getTokenAt(cur);
65 | if (cur.ch == token.start + 1 && token.string.charAt(0) == '.') {
66 | token.end = token.start;
67 | token.string = '.';
68 | token.className = "property";
69 | }
70 | else if (/^\.[\w$_]*$/.test(token.string)) {
71 | token.className = "property";
72 | token.start++;
73 | token.string = token.string.replace(/\./, '');
74 | }
75 | return token;
76 | }
77 |
78 | CodeMirror.coffeescriptHint = function(editor) {
79 | return scriptHint(editor, coffeescriptKeywords, getCoffeeScriptToken);
80 | }
81 |
82 | var stringProps = ("charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight " +
83 | "toUpperCase toLowerCase split concat match replace search").split(" ");
84 | var arrayProps = ("length concat join splice push pop shift unshift slice reverse sort indexOf " +
85 | "lastIndexOf every some filter forEach map reduce reduceRight ").split(" ");
86 | var funcProps = "prototype apply call bind".split(" ");
87 | var javascriptKeywords = ("break case catch continue debugger default delete do else false finally for function " +
88 | "if in instanceof new null return switch throw true try typeof var void while with").split(" ");
89 | var coffeescriptKeywords = ("and break catch class continue delete do else extends false finally for " +
90 | "if in instanceof isnt new no not null of off on or return switch then throw true try typeof until void while with yes").split(" ");
91 |
92 | function getCompletions(token, context, keywords) {
93 | var found = [], start = token.string;
94 | function maybeAdd(str) {
95 | if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str);
96 | }
97 | function gatherCompletions(obj) {
98 | if (typeof obj == "string") forEach(stringProps, maybeAdd);
99 | else if (obj instanceof Array) forEach(arrayProps, maybeAdd);
100 | else if (obj instanceof Function) forEach(funcProps, maybeAdd);
101 | for (var name in obj) maybeAdd(name);
102 | }
103 |
104 | if (context) {
105 | // If this is a property, see if it belongs to some object we can
106 | // find in the current environment.
107 | var obj = context.pop(), base;
108 | if (obj.className == "variable")
109 | base = window[obj.string];
110 | else if (obj.className == "string")
111 | base = "";
112 | else if (obj.className == "atom")
113 | base = 1;
114 | else if (obj.className == "function") {
115 | if (window.jQuery != null && (obj.string == '$' || obj.string == 'jQuery') &&
116 | (typeof window.jQuery == 'function'))
117 | base = window.jQuery();
118 | else if (window._ != null && (obj.string == '_') && (typeof window._ == 'function'))
119 | base = window._();
120 | }
121 | while (base != null && context.length)
122 | base = base[context.pop().string];
123 | if (base != null) gatherCompletions(base);
124 | }
125 | else {
126 | // If not, just look in the window object and any local scope
127 | // (reading into JS mode internals to get at the local variables)
128 | for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name);
129 | gatherCompletions(window);
130 | forEach(keywords, maybeAdd);
131 | }
132 | return found;
133 | }
134 | })();
135 |
--------------------------------------------------------------------------------
/docs/guide/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | REM Command file for Sphinx documentation
4 |
5 | if "%SPHINXBUILD%" == "" (
6 | set SPHINXBUILD=sphinx-build
7 | )
8 | set BUILDDIR=_build
9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
10 | set I18NSPHINXOPTS=%SPHINXOPTS% .
11 | if NOT "%PAPER%" == "" (
12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
14 | )
15 |
16 | if "%1" == "" goto help
17 |
18 | if "%1" == "help" (
19 | :help
20 | echo.Please use `make ^` where ^ is one of
21 | echo. html to make standalone HTML files
22 | echo. dirhtml to make HTML files named index.html in directories
23 | echo. singlehtml to make a single large HTML file
24 | echo. pickle to make pickle files
25 | echo. json to make JSON files
26 | echo. htmlhelp to make HTML files and a HTML help project
27 | echo. qthelp to make HTML files and a qthelp project
28 | echo. devhelp to make HTML files and a Devhelp project
29 | echo. epub to make an epub
30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
31 | echo. text to make text files
32 | echo. man to make manual pages
33 | echo. texinfo to make Texinfo files
34 | echo. gettext to make PO message catalogs
35 | echo. changes to make an overview over all changed/added/deprecated items
36 | echo. linkcheck to check all external links for integrity
37 | echo. doctest to run all doctests embedded in the documentation if enabled
38 | goto end
39 | )
40 |
41 | if "%1" == "clean" (
42 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
43 | del /q /s %BUILDDIR%\*
44 | goto end
45 | )
46 |
47 | if "%1" == "html" (
48 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
49 | if errorlevel 1 exit /b 1
50 | echo.
51 | echo.Build finished. The HTML pages are in %BUILDDIR%/html.
52 | goto end
53 | )
54 |
55 | if "%1" == "dirhtml" (
56 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
57 | if errorlevel 1 exit /b 1
58 | echo.
59 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
60 | goto end
61 | )
62 |
63 | if "%1" == "singlehtml" (
64 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
65 | if errorlevel 1 exit /b 1
66 | echo.
67 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
68 | goto end
69 | )
70 |
71 | if "%1" == "pickle" (
72 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
73 | if errorlevel 1 exit /b 1
74 | echo.
75 | echo.Build finished; now you can process the pickle files.
76 | goto end
77 | )
78 |
79 | if "%1" == "json" (
80 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
81 | if errorlevel 1 exit /b 1
82 | echo.
83 | echo.Build finished; now you can process the JSON files.
84 | goto end
85 | )
86 |
87 | if "%1" == "htmlhelp" (
88 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
89 | if errorlevel 1 exit /b 1
90 | echo.
91 | echo.Build finished; now you can run HTML Help Workshop with the ^
92 | .hhp project file in %BUILDDIR%/htmlhelp.
93 | goto end
94 | )
95 |
96 | if "%1" == "qthelp" (
97 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
98 | if errorlevel 1 exit /b 1
99 | echo.
100 | echo.Build finished; now you can run "qcollectiongenerator" with the ^
101 | .qhcp project file in %BUILDDIR%/qthelp, like this:
102 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Roy.qhcp
103 | echo.To view the help file:
104 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Roy.ghc
105 | goto end
106 | )
107 |
108 | if "%1" == "devhelp" (
109 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
110 | if errorlevel 1 exit /b 1
111 | echo.
112 | echo.Build finished.
113 | goto end
114 | )
115 |
116 | if "%1" == "epub" (
117 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
118 | if errorlevel 1 exit /b 1
119 | echo.
120 | echo.Build finished. The epub file is in %BUILDDIR%/epub.
121 | goto end
122 | )
123 |
124 | if "%1" == "latex" (
125 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
126 | if errorlevel 1 exit /b 1
127 | echo.
128 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
129 | goto end
130 | )
131 |
132 | if "%1" == "text" (
133 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
134 | if errorlevel 1 exit /b 1
135 | echo.
136 | echo.Build finished. The text files are in %BUILDDIR%/text.
137 | goto end
138 | )
139 |
140 | if "%1" == "man" (
141 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
142 | if errorlevel 1 exit /b 1
143 | echo.
144 | echo.Build finished. The manual pages are in %BUILDDIR%/man.
145 | goto end
146 | )
147 |
148 | if "%1" == "texinfo" (
149 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
150 | if errorlevel 1 exit /b 1
151 | echo.
152 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
153 | goto end
154 | )
155 |
156 | if "%1" == "gettext" (
157 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
158 | if errorlevel 1 exit /b 1
159 | echo.
160 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
161 | goto end
162 | )
163 |
164 | if "%1" == "changes" (
165 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
166 | if errorlevel 1 exit /b 1
167 | echo.
168 | echo.The overview file is in %BUILDDIR%/changes.
169 | goto end
170 | )
171 |
172 | if "%1" == "linkcheck" (
173 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
174 | if errorlevel 1 exit /b 1
175 | echo.
176 | echo.Link check complete; look for any errors in the above output ^
177 | or in %BUILDDIR%/linkcheck/output.txt.
178 | goto end
179 | )
180 |
181 | if "%1" == "doctest" (
182 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
183 | if errorlevel 1 exit /b 1
184 | echo.
185 | echo.Testing of doctests in the sources finished, look at the ^
186 | results in %BUILDDIR%/doctest/output.txt.
187 | goto end
188 | )
189 |
190 | :end
191 |
--------------------------------------------------------------------------------
/docs/guide/Makefile:
--------------------------------------------------------------------------------
1 | # Makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line.
5 | SPHINXOPTS =
6 | SPHINXBUILD = sphinx-build
7 | PAPER =
8 | BUILDDIR = _build
9 |
10 | # Internal variables.
11 | PAPEROPT_a4 = -D latex_paper_size=a4
12 | PAPEROPT_letter = -D latex_paper_size=letter
13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
14 | # the i18n builder cannot share the environment and doctrees with the others
15 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
16 |
17 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
18 |
19 | help:
20 | @echo "Please use \`make ' where is one of"
21 | @echo " html to make standalone HTML files"
22 | @echo " dirhtml to make HTML files named index.html in directories"
23 | @echo " singlehtml to make a single large HTML file"
24 | @echo " pickle to make pickle files"
25 | @echo " json to make JSON files"
26 | @echo " htmlhelp to make HTML files and a HTML help project"
27 | @echo " qthelp to make HTML files and a qthelp project"
28 | @echo " devhelp to make HTML files and a Devhelp project"
29 | @echo " epub to make an epub"
30 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
31 | @echo " latexpdf to make LaTeX files and run them through pdflatex"
32 | @echo " text to make text files"
33 | @echo " man to make manual pages"
34 | @echo " texinfo to make Texinfo files"
35 | @echo " info to make Texinfo files and run them through makeinfo"
36 | @echo " gettext to make PO message catalogs"
37 | @echo " changes to make an overview of all changed/added/deprecated items"
38 | @echo " linkcheck to check all external links for integrity"
39 | @echo " doctest to run all doctests embedded in the documentation (if enabled)"
40 |
41 | clean:
42 | -rm -rf $(BUILDDIR)/*
43 |
44 | html:
45 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
46 | @echo
47 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
48 |
49 | dirhtml:
50 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
51 | @echo
52 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
53 |
54 | singlehtml:
55 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
56 | @echo
57 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
58 |
59 | pickle:
60 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
61 | @echo
62 | @echo "Build finished; now you can process the pickle files."
63 |
64 | json:
65 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
66 | @echo
67 | @echo "Build finished; now you can process the JSON files."
68 |
69 | htmlhelp:
70 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
71 | @echo
72 | @echo "Build finished; now you can run HTML Help Workshop with the" \
73 | ".hhp project file in $(BUILDDIR)/htmlhelp."
74 |
75 | qthelp:
76 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
77 | @echo
78 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \
79 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
80 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Roy.qhcp"
81 | @echo "To view the help file:"
82 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Roy.qhc"
83 |
84 | devhelp:
85 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
86 | @echo
87 | @echo "Build finished."
88 | @echo "To view the help file:"
89 | @echo "# mkdir -p $$HOME/.local/share/devhelp/Roy"
90 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Roy"
91 | @echo "# devhelp"
92 |
93 | epub:
94 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
95 | @echo
96 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
97 |
98 | latex:
99 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
100 | @echo
101 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
102 | @echo "Run \`make' in that directory to run these through (pdf)latex" \
103 | "(use \`make latexpdf' here to do that automatically)."
104 |
105 | latexpdf:
106 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
107 | @echo "Running LaTeX files through pdflatex..."
108 | $(MAKE) -C $(BUILDDIR)/latex all-pdf
109 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
110 |
111 | text:
112 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
113 | @echo
114 | @echo "Build finished. The text files are in $(BUILDDIR)/text."
115 |
116 | man:
117 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
118 | @echo
119 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
120 |
121 | texinfo:
122 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
123 | @echo
124 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
125 | @echo "Run \`make' in that directory to run these through makeinfo" \
126 | "(use \`make info' here to do that automatically)."
127 |
128 | info:
129 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
130 | @echo "Running Texinfo files through makeinfo..."
131 | make -C $(BUILDDIR)/texinfo info
132 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
133 |
134 | gettext:
135 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
136 | @echo
137 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
138 |
139 | changes:
140 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
141 | @echo
142 | @echo "The overview file is in $(BUILDDIR)/changes."
143 |
144 | linkcheck:
145 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
146 | @echo
147 | @echo "Link check complete; look for any errors in the above output " \
148 | "or in $(BUILDDIR)/linkcheck/output.txt."
149 |
150 | doctest:
151 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
152 | @echo "Testing of doctests in the sources finished, look at the " \
153 | "results in $(BUILDDIR)/doctest/output.txt."
154 |
--------------------------------------------------------------------------------
/site/codemirror2/lib/util/closetag.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Tag-closer extension for CodeMirror.
3 | *
4 | * This extension adds a "closeTag" utility function that can be used with key bindings to
5 | * insert a matching end tag after the ">" character of a start tag has been typed. It can
6 | * also complete "" if a matching start tag is found. It will correctly ignore signal
7 | * characters for empty tags, comments, CDATA, etc.
8 | *
9 | * The function depends on internal parser state to identify tags. It is compatible with the
10 | * following CodeMirror modes and will ignore all others:
11 | * - htmlmixed
12 | * - xml
13 | * - xmlpure
14 | *
15 | * See demos/closetag.html for a usage example.
16 | *
17 | * @author Nathan Williams
18 | * Contributed under the same license terms as CodeMirror.
19 | */
20 | (function() {
21 | /** Option that allows tag closing behavior to be toggled. Default is true. */
22 | CodeMirror.defaults['closeTagEnabled'] = true;
23 |
24 | /** Array of tag names to add indentation after the start tag for. Default is the list of block-level html tags. */
25 | CodeMirror.defaults['closeTagIndent'] = ['applet', 'blockquote', 'body', 'button', 'div', 'dl', 'fieldset', 'form', 'frameset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'html', 'iframe', 'layer', 'legend', 'object', 'ol', 'p', 'select', 'table', 'ul'];
26 |
27 | /**
28 | * Call during key processing to close tags. Handles the key event if the tag is closed, otherwise throws CodeMirror.Pass.
29 | * - cm: The editor instance.
30 | * - ch: The character being processed.
31 | * - indent: Optional. Omit or pass true to use the default indentation tag list defined in the 'closeTagIndent' option.
32 | * Pass false to disable indentation. Pass an array to override the default list of tag names.
33 | */
34 | CodeMirror.defineExtension("closeTag", function(cm, ch, indent) {
35 | if (!cm.getOption('closeTagEnabled')) {
36 | throw CodeMirror.Pass;
37 | }
38 |
39 | var mode = cm.getOption('mode');
40 |
41 | if (mode == 'text/html') {
42 |
43 | /*
44 | * Relevant structure of token:
45 | *
46 | * htmlmixed
47 | * className
48 | * state
49 | * htmlState
50 | * type
51 | * context
52 | * tagName
53 | * mode
54 | *
55 | * xml
56 | * className
57 | * state
58 | * tagName
59 | * type
60 | */
61 |
62 | var pos = cm.getCursor();
63 | var tok = cm.getTokenAt(pos);
64 | var state = tok.state;
65 |
66 | if (state.mode && state.mode != 'html') {
67 | throw CodeMirror.Pass; // With htmlmixed, we only care about the html sub-mode.
68 | }
69 |
70 | if (ch == '>') {
71 | var type = state.htmlState ? state.htmlState.type : state.type; // htmlmixed : xml
72 |
73 | if (tok.className == 'tag' && type == 'closeTag') {
74 | throw CodeMirror.Pass; // Don't process the '>' at the end of an end-tag.
75 | }
76 |
77 | cm.replaceSelection('>'); // Mode state won't update until we finish the tag.
78 | pos = {line: pos.line, ch: pos.ch + 1};
79 | cm.setCursor(pos);
80 |
81 | tok = cm.getTokenAt(cm.getCursor());
82 | state = tok.state;
83 | type = state.htmlState ? state.htmlState.type : state.type; // htmlmixed : xml
84 |
85 | if (tok.className == 'tag' && type != 'selfcloseTag') {
86 | var tagName = state.htmlState ? state.htmlState.context.tagName : state.tagName; // htmlmixed : xml
87 | if (tagName.length > 0) {
88 | insertEndTag(cm, indent, pos, tagName);
89 | }
90 | return;
91 | }
92 |
93 | // Undo the '>' insert and allow cm to handle the key instead.
94 | cm.setSelection({line: pos.line, ch: pos.ch - 1}, pos);
95 | cm.replaceSelection("");
96 |
97 | } else if (ch == '/') {
98 | if (tok.className == 'tag' && tok.string == '<') {
99 | var tagName = state.htmlState ? (state.htmlState.context ? state.htmlState.context.tagName : '') : state.context.tagName; // htmlmixed : xml # extra htmlmized check is for '' edge case
100 | if (tagName.length > 0) {
101 | completeEndTag(cm, pos, tagName);
102 | return;
103 | }
104 | }
105 | }
106 |
107 | } else if (mode == 'xmlpure') {
108 |
109 | var pos = cm.getCursor();
110 | var tok = cm.getTokenAt(pos);
111 | var tagName = tok.state.context.tagName;
112 |
113 | if (ch == '>') {
114 | // tagName=foo, string=foo
115 | // tagName=foo, string=/ # ignore
116 | // tagName=foo, string=/foo # ignore
117 | if (tok.string == tagName) {
118 | cm.replaceSelection('>'); // parity w/html modes
119 | pos = {line: pos.line, ch: pos.ch + 1};
120 | cm.setCursor(pos);
121 |
122 | insertEndTag(cm, indent, pos, tagName);
123 | return;
124 | }
125 |
126 | } else if (ch == '/') {
127 | // tagName=foo, string=<
129 | if (tok.string == '<') {
130 | completeEndTag(cm, pos, tagName);
131 | return;
132 | }
133 | }
134 | }
135 |
136 | throw CodeMirror.Pass; // Bubble if not handled
137 | });
138 |
139 | function insertEndTag(cm, indent, pos, tagName) {
140 | if (shouldIndent(cm, indent, tagName)) {
141 | cm.replaceSelection('\n\n' + tagName + '>', 'end');
142 | cm.indentLine(pos.line + 1);
143 | cm.indentLine(pos.line + 2);
144 | cm.setCursor({line: pos.line + 1, ch: cm.getLine(pos.line + 1).length});
145 | } else {
146 | cm.replaceSelection('' + tagName + '>');
147 | cm.setCursor(pos);
148 | }
149 | }
150 |
151 | function shouldIndent(cm, indent, tagName) {
152 | if (typeof indent == 'undefined' || indent == null || indent == true) {
153 | indent = cm.getOption('closeTagIndent');
154 | }
155 | if (!indent) {
156 | indent = [];
157 | }
158 | return indexOf(indent, tagName.toLowerCase()) != -1;
159 | }
160 |
161 | // C&P from codemirror.js...would be nice if this were visible to utilities.
162 | function indexOf(collection, elt) {
163 | if (collection.indexOf) return collection.indexOf(elt);
164 | for (var i = 0, e = collection.length; i < e; ++i)
165 | if (collection[i] == elt) return i;
166 | return -1;
167 | }
168 |
169 | function completeEndTag(cm, pos, tagName) {
170 | cm.replaceSelection('/' + tagName + '>');
171 | cm.setCursor({line: pos.line, ch: pos.ch + tagName.length + 2 });
172 | }
173 |
174 | })();
175 |
--------------------------------------------------------------------------------
/site/codemirror2/lib/util/foldcode.js:
--------------------------------------------------------------------------------
1 | // the tagRangeFinder function is
2 | // Copyright (C) 2011 by Daniel Glazman
3 | // released under the MIT license (../../LICENSE) like the rest of CodeMirror
4 | CodeMirror.tagRangeFinder = function(cm, line) {
5 | var nameStartChar = "A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD";
6 | var nameChar = nameStartChar + "\-\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040";
7 | var xmlNAMERegExp = new RegExp("^[" + nameStartChar + "][" + nameChar + "]*");
8 |
9 | var lineText = cm.getLine(line);
10 | var found = false;
11 | var tag = null;
12 | var pos = 0;
13 | while (!found) {
14 | pos = lineText.indexOf("<", pos);
15 | if (-1 == pos) // no tag on line
16 | return;
17 | if (pos + 1 < lineText.length && lineText[pos + 1] == "/") { // closing tag
18 | pos++;
19 | continue;
20 | }
21 | // ok we weem to have a start tag
22 | if (!lineText.substr(pos + 1).match(xmlNAMERegExp)) { // not a tag name...
23 | pos++;
24 | continue;
25 | }
26 | var gtPos = lineText.indexOf(">", pos + 1);
27 | if (-1 == gtPos) { // end of start tag not in line
28 | var l = line + 1;
29 | var foundGt = false;
30 | var lastLine = cm.lineCount();
31 | while (l < lastLine && !foundGt) {
32 | var lt = cm.getLine(l);
33 | var gt = lt.indexOf(">");
34 | if (-1 != gt) { // found a >
35 | foundGt = true;
36 | var slash = lt.lastIndexOf("/", gt);
37 | if (-1 != slash && slash < gt) {
38 | var str = lineText.substr(slash, gt - slash + 1);
39 | if (!str.match( /\/\s*\>/ )) // yep, that's the end of empty tag
40 | return l+1;
41 | }
42 | }
43 | l++;
44 | }
45 | found = true;
46 | }
47 | else {
48 | var slashPos = lineText.lastIndexOf("/", gtPos);
49 | if (-1 == slashPos) { // cannot be empty tag
50 | found = true;
51 | // don't continue
52 | }
53 | else { // empty tag?
54 | // check if really empty tag
55 | var str = lineText.substr(slashPos, gtPos - slashPos + 1);
56 | if (!str.match( /\/\s*\>/ )) { // finally not empty
57 | found = true;
58 | // don't continue
59 | }
60 | }
61 | }
62 | if (found) {
63 | var subLine = lineText.substr(pos + 1);
64 | tag = subLine.match(xmlNAMERegExp);
65 | if (tag) {
66 | // we have an element name, wooohooo !
67 | tag = tag[0];
68 | // do we have the close tag on same line ???
69 | if (-1 != lineText.indexOf("" + tag + ">", pos)) // yep
70 | {
71 | found = false;
72 | }
73 | // we don't, so we have a candidate...
74 | }
75 | else
76 | found = false;
77 | }
78 | if (!found)
79 | pos++;
80 | }
81 |
82 | if (found) {
83 | var startTag = "(\\<\\/" + tag + "\\>)|(\\<" + tag + "\\>)|(\\<" + tag + "\\s)|(\\<" + tag + "$)";
84 | var startTagRegExp = new RegExp(startTag, "g");
85 | var endTag = "" + tag + ">";
86 | var depth = 1;
87 | var l = line + 1;
88 | var lastLine = cm.lineCount();
89 | while (l < lastLine) {
90 | lineText = cm.getLine(l);
91 | var match = lineText.match(startTagRegExp);
92 | if (match) {
93 | for (var i = 0; i < match.length; i++) {
94 | if (match[i] == endTag)
95 | depth--;
96 | else
97 | depth++;
98 | if (!depth)
99 | return l+1;
100 | }
101 | }
102 | l++;
103 | }
104 | return;
105 | }
106 | };
107 |
108 | CodeMirror.braceRangeFinder = function(cm, line) {
109 | var lineText = cm.getLine(line);
110 | var startChar = lineText.lastIndexOf("{");
111 | if (startChar < 0 || lineText.lastIndexOf("}") > startChar) return;
112 | var tokenType = cm.getTokenAt({line: line, ch: startChar}).className;
113 | var count = 1, lastLine = cm.lineCount(), end;
114 | outer: for (var i = line + 1; i < lastLine; ++i) {
115 | var text = cm.getLine(i), pos = 0;
116 | for (;;) {
117 | var nextOpen = text.indexOf("{", pos), nextClose = text.indexOf("}", pos);
118 | if (nextOpen < 0) nextOpen = text.length;
119 | if (nextClose < 0) nextClose = text.length;
120 | pos = Math.min(nextOpen, nextClose);
121 | if (pos == text.length) break;
122 | if (cm.getTokenAt({line: i, ch: pos + 1}).className == tokenType) {
123 | if (pos == nextOpen) ++count;
124 | else if (!--count) { end = i; break outer; }
125 | }
126 | ++pos;
127 | }
128 | }
129 | if (end == null || end == line + 1) return;
130 | return end;
131 | };
132 |
133 | CodeMirror.indentRangeFinder = function(cm, line) {
134 | var tabSize = cm.getOption("tabSize");
135 | var myIndent = cm.getLineHandle(line).indentation(tabSize), last;
136 | for (var i = line + 1, end = cm.lineCount(); i < end; ++i) {
137 | var handle = cm.getLineHandle(i);
138 | if (!/^\s*$/.test(handle.text)) {
139 | if (handle.indentation(tabSize) <= myIndent) break;
140 | last = i;
141 | }
142 | }
143 | if (!last) return null;
144 | return last + 1;
145 | };
146 |
147 | CodeMirror.newFoldFunction = function(rangeFinder, markText) {
148 | var folded = [];
149 | if (markText == null) markText = '
▼
%N%';
150 |
151 | function isFolded(cm, n) {
152 | for (var i = 0; i < folded.length; ++i) {
153 | var start = cm.lineInfo(folded[i].start);
154 | if (!start) folded.splice(i--, 1);
155 | else if (start.line == n) return {pos: i, region: folded[i]};
156 | }
157 | }
158 |
159 | function expand(cm, region) {
160 | cm.clearMarker(region.start);
161 | for (var i = 0; i < region.hidden.length; ++i)
162 | cm.showLine(region.hidden[i]);
163 | }
164 |
165 | return function(cm, line) {
166 | cm.operation(function() {
167 | var known = isFolded(cm, line);
168 | if (known) {
169 | folded.splice(known.pos, 1);
170 | expand(cm, known.region);
171 | } else {
172 | var end = rangeFinder(cm, line);
173 | if (end == null) return;
174 | var hidden = [];
175 | for (var i = line + 1; i < end; ++i) {
176 | var handle = cm.hideLine(i);
177 | if (handle) hidden.push(handle);
178 | }
179 | var first = cm.setMarker(line, markText);
180 | var region = {start: first, hidden: hidden};
181 | cm.onDeleteLine(first, function() { expand(cm, region); });
182 | folded.push(region);
183 | }
184 | });
185 | };
186 | };
187 |
--------------------------------------------------------------------------------
/docs/guide/conf.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Roy documentation build configuration file, created by
4 | # sphinx-quickstart on Sat Jan 7 18:12:13 2012.
5 | #
6 | # This file is execfile()d with the current directory set to its containing dir.
7 | #
8 | # Note that not all possible configuration values are present in this
9 | # autogenerated file.
10 | #
11 | # All configuration values have a default; values that are commented out
12 | # serve to show the default.
13 |
14 | import sys, os
15 |
16 | # If extensions (or modules to document with autodoc) are in another directory,
17 | # add these directories to sys.path here. If the directory is relative to the
18 | # documentation root, use os.path.abspath to make it absolute, like shown here.
19 | #sys.path.insert(0, os.path.abspath('.'))
20 |
21 | # -- General configuration -----------------------------------------------------
22 |
23 | # If your documentation needs a minimal Sphinx version, state it here.
24 | #needs_sphinx = '1.0'
25 |
26 | # Add any Sphinx extension module names here, as strings. They can be extensions
27 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
28 | extensions = []
29 |
30 | # Add any paths that contain templates here, relative to this directory.
31 | templates_path = ['_templates']
32 |
33 | # The suffix of source filenames.
34 | source_suffix = '.rst'
35 |
36 | # The encoding of source files.
37 | #source_encoding = 'utf-8-sig'
38 |
39 | # The master toctree document.
40 | master_doc = 'index'
41 |
42 | # General information about the project.
43 | project = u'Roy'
44 | copyright = u'2012, Brian McKenna'
45 |
46 | # The version info for the project you're documenting, acts as replacement for
47 | # |version| and |release|, also used in various other places throughout the
48 | # built documents.
49 | #
50 | # The short X.Y version.
51 | version = '0.1'
52 | # The full version, including alpha/beta/rc tags.
53 | release = '0.1'
54 |
55 | # The language for content autogenerated by Sphinx. Refer to documentation
56 | # for a list of supported languages.
57 | #language = None
58 |
59 | # There are two options for replacing |today|: either, you set today to some
60 | # non-false value, then it is used:
61 | #today = ''
62 | # Else, today_fmt is used as the format for a strftime call.
63 | #today_fmt = '%B %d, %Y'
64 |
65 | # List of patterns, relative to source directory, that match files and
66 | # directories to ignore when looking for source files.
67 | exclude_patterns = ['_build']
68 |
69 | # The reST default role (used for this markup: `text`) to use for all documents.
70 | #default_role = None
71 |
72 | # If true, '()' will be appended to :func: etc. cross-reference text.
73 | #add_function_parentheses = True
74 |
75 | # If true, the current module name will be prepended to all description
76 | # unit titles (such as .. function::).
77 | #add_module_names = True
78 |
79 | # If true, sectionauthor and moduleauthor directives will be shown in the
80 | # output. They are ignored by default.
81 | #show_authors = False
82 |
83 | # The name of the Pygments (syntax highlighting) style to use.
84 | pygments_style = 'sphinx'
85 |
86 | # A list of ignored prefixes for module index sorting.
87 | #modindex_common_prefix = []
88 |
89 |
90 | # -- Options for HTML output ---------------------------------------------------
91 |
92 | # The theme to use for HTML and HTML Help pages. See the documentation for
93 | # a list of builtin themes.
94 | html_theme = 'default'
95 |
96 | # Theme options are theme-specific and customize the look and feel of a theme
97 | # further. For a list of options available for each theme, see the
98 | # documentation.
99 | #html_theme_options = {}
100 |
101 | # Add any paths that contain custom themes here, relative to this directory.
102 | #html_theme_path = []
103 |
104 | # The name for this set of Sphinx documents. If None, it defaults to
105 | # " v documentation".
106 | #html_title = None
107 |
108 | # A shorter title for the navigation bar. Default is the same as html_title.
109 | #html_short_title = None
110 |
111 | # The name of an image file (relative to this directory) to place at the top
112 | # of the sidebar.
113 | #html_logo = None
114 |
115 | # The name of an image file (within the static path) to use as favicon of the
116 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
117 | # pixels large.
118 | #html_favicon = None
119 |
120 | # Add any paths that contain custom static files (such as style sheets) here,
121 | # relative to this directory. They are copied after the builtin static files,
122 | # so a file named "default.css" will overwrite the builtin "default.css".
123 | html_static_path = ['_static']
124 |
125 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
126 | # using the given strftime format.
127 | #html_last_updated_fmt = '%b %d, %Y'
128 |
129 | # If true, SmartyPants will be used to convert quotes and dashes to
130 | # typographically correct entities.
131 | #html_use_smartypants = True
132 |
133 | # Custom sidebar templates, maps document names to template names.
134 | #html_sidebars = {}
135 |
136 | # Additional templates that should be rendered to pages, maps page names to
137 | # template names.
138 | #html_additional_pages = {}
139 |
140 | # If false, no module index is generated.
141 | #html_domain_indices = True
142 |
143 | # If false, no index is generated.
144 | #html_use_index = True
145 |
146 | # If true, the index is split into individual pages for each letter.
147 | #html_split_index = False
148 |
149 | # If true, links to the reST sources are added to the pages.
150 | #html_show_sourcelink = True
151 |
152 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
153 | #html_show_sphinx = True
154 |
155 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
156 | #html_show_copyright = True
157 |
158 | # If true, an OpenSearch description file will be output, and all pages will
159 | # contain a tag referring to it. The value of this option must be the
160 | # base URL from which the finished HTML is served.
161 | #html_use_opensearch = ''
162 |
163 | # This is the file name suffix for HTML files (e.g. ".xhtml").
164 | #html_file_suffix = None
165 |
166 | # Output file base name for HTML help builder.
167 | htmlhelp_basename = 'Roydoc'
168 |
169 |
170 | # -- Options for LaTeX output --------------------------------------------------
171 |
172 | latex_elements = {
173 | # The paper size ('letterpaper' or 'a4paper').
174 | #'papersize': 'letterpaper',
175 |
176 | # The font size ('10pt', '11pt' or '12pt').
177 | #'pointsize': '10pt',
178 |
179 | # Additional stuff for the LaTeX preamble.
180 | #'preamble': '',
181 | }
182 |
183 | # Grouping the document tree into LaTeX files. List of tuples
184 | # (source start file, target name, title, author, documentclass [howto/manual]).
185 | latex_documents = [
186 | ('index', 'Roy.tex', u'Roy Documentation',
187 | u'Brian McKenna', 'manual'),
188 | ]
189 |
190 | # The name of an image file (relative to this directory) to place at the top of
191 | # the title page.
192 | #latex_logo = None
193 |
194 | # For "manual" documents, if this is true, then toplevel headings are parts,
195 | # not chapters.
196 | #latex_use_parts = False
197 |
198 | # If true, show page references after internal links.
199 | #latex_show_pagerefs = False
200 |
201 | # If true, show URL addresses after external links.
202 | #latex_show_urls = False
203 |
204 | # Documents to append as an appendix to all manuals.
205 | #latex_appendices = []
206 |
207 | # If false, no module index is generated.
208 | #latex_domain_indices = True
209 |
210 |
211 | # -- Options for manual page output --------------------------------------------
212 |
213 | # One entry per manual page. List of tuples
214 | # (source start file, name, description, authors, manual section).
215 | man_pages = [
216 | ('index', 'roy', u'Roy Documentation',
217 | [u'Brian McKenna'], 1)
218 | ]
219 |
220 | # If true, show URL addresses after external links.
221 | #man_show_urls = False
222 |
223 |
224 | # -- Options for Texinfo output ------------------------------------------------
225 |
226 | # Grouping the document tree into Texinfo files. List of tuples
227 | # (source start file, target name, title, author,
228 | # dir menu entry, description, category)
229 | texinfo_documents = [
230 | ('index', 'Roy', u'Roy Documentation',
231 | u'Brian McKenna', 'Roy', 'One line description of project.',
232 | 'Miscellaneous'),
233 | ]
234 |
235 | # Documents to append as an appendix to all manuals.
236 | #texinfo_appendices = []
237 |
238 | # If false, no module index is generated.
239 | #texinfo_domain_indices = True
240 |
241 | # How to display URL addresses: 'footnote', 'no', or 'inline'.
242 | #texinfo_show_urls = 'footnote'
243 |
--------------------------------------------------------------------------------
/src/lexer.js:
--------------------------------------------------------------------------------
1 | var unicode = require('unicode-categories'),
2 | _ = require('underscore');
3 |
4 | // http://es5.github.com/#x7.6
5 | // ECMAscript identifier starts with `$`, `_`,
6 | // or letter from (Lu Ll Lt Lm Lo Nl) unicode groups.
7 | // Then identifier can also be from groups (Nd, Mn, Mc, or Pc).
8 | // Roy identifier cannot have letter u03BB (greek lowercase lambda)
9 | // because it's used in anonymous functions.
10 | var IDENTIFIER = new RegExp(
11 | unicode.ECMA.identifier.source.replace('\\u03BB', '')
12 | );
13 |
14 | var NUMBER = /^-?[0-9]+(\.[0-9]+)?([eE][\-\+]?[0-9]+)?/;
15 | var STRING = /^(?:"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')/;
16 | var COMMENT = /^\/\/.*/;
17 | var WHITESPACE = /^[^\n\S]+/;
18 | var INDENT = /^(?:\n[^\n\S]*)+/;
19 | var GENERIC = /^#([a-z]+)/;
20 | var SHEBANG = /^#!.*/;
21 |
22 | var keywordTokens = {
23 | 'true': 'BOOLEAN',
24 | 'false': 'BOOLEAN',
25 | 'Function': 'FUNCTION',
26 | 'let': 'LET',
27 | 'if': 'IF',
28 | 'instance': 'INSTANCE',
29 | 'then': 'THEN',
30 | 'else': 'ELSE',
31 | 'data': 'DATA',
32 | 'type': 'TYPE',
33 | 'typeclass': 'TYPECLASS',
34 | 'match': 'MATCH',
35 | 'case': 'CASE',
36 | 'do': 'DO',
37 | 'return': 'RETURN',
38 | 'with': 'WITH',
39 | 'where': 'WHERE'
40 | };
41 |
42 | var indent;
43 | var indents;
44 | var tokens;
45 | var lineno;
46 |
47 | var identifierToken = function(chunk) {
48 | var token = IDENTIFIER.exec(chunk);
49 |
50 | if(token) {
51 | var value = token[0],
52 | name = keywordTokens[value] || 'IDENTIFIER';
53 |
54 | tokens.push([name, value, lineno]);
55 | return token[0].length;
56 | }
57 | return 0;
58 | };
59 |
60 | var numberToken = function(chunk) {
61 | var token = NUMBER.exec(chunk);
62 | if(token) {
63 | tokens.push(['NUMBER', token[0], lineno]);
64 | return token[0].length;
65 | }
66 | return 0;
67 | };
68 |
69 | var stringToken = function(chunk) {
70 | var token = STRING.exec(chunk);
71 | if (token) {
72 | tokens.push(['STRING', token[0], lineno]);
73 | return token[0].length;
74 |
75 | }
76 | return 0;
77 | };
78 |
79 | var genericToken = function(chunk) {
80 | var token = GENERIC.exec(chunk);
81 | if(token) {
82 | tokens.push(['GENERIC', token[1], lineno]);
83 | return token[0].length;
84 | }
85 | return 0;
86 | };
87 |
88 | var commentToken = function(chunk) {
89 | var token = COMMENT.exec(chunk);
90 | if(token) {
91 | tokens.push(['COMMENT', token[0], lineno]);
92 | return token[0].length;
93 | }
94 | return 0;
95 | };
96 |
97 | var whitespaceToken = function(chunk) {
98 | var token = WHITESPACE.exec(chunk);
99 | if(token) {
100 | return token[0].length;
101 | }
102 | return 0;
103 | };
104 |
105 | // If an OUTDENT is followed by a line continuer,
106 | // the next TERMINATOR token is supressed.
107 | var lineContinuer = {
108 | "where": true
109 | };
110 |
111 | var lineToken = function(chunk) {
112 | var token = INDENT.exec(chunk);
113 | if(token) {
114 | var lastNewline = token[0].lastIndexOf("\n") + 1;
115 | var size = token[0].length - lastNewline;
116 | if(size > indent) {
117 | indents.push(size);
118 | tokens.push(['INDENT', size - indent, lineno]);
119 | } else {
120 | if(size < indent) {
121 | var last = indents[indents.length - 1];
122 | while(size < last) {
123 | tokens.push(['OUTDENT', last - size, lineno]);
124 | indents.pop();
125 | last = indents[indents.length - 1];
126 | }
127 | }
128 | if(tokens.length > 0) {
129 | var lookahead = IDENTIFIER.exec(chunk.slice(token[0].length));
130 |
131 | if (!lookahead || !lineContinuer[lookahead[0]]) {
132 | tokens.push(['TERMINATOR', token[0].substring(0, lastNewline), lineno]);
133 | }
134 | }
135 | }
136 |
137 | indent = size;
138 | return token[0].length;
139 | }
140 | return 0;
141 | };
142 |
143 | var literalToken = function(chunk) {
144 | var tag = chunk.slice(0, 1);
145 | var next;
146 | switch(tag) {
147 | case '<':
148 | next = chunk.slice(0, 2);
149 | if(next == '<=') {
150 | tokens.push(['COMPARE', next, lineno]);
151 | return 2;
152 | } else if(next == '<-') {
153 | tokens.push(['LEFTARROW', next, lineno]);
154 | return 2;
155 | } else if(next == '<<') {
156 | tokens.push(['MATH', next, lineno]);
157 | return 2;
158 | }
159 | tokens.push(['COMPARE', tag, lineno]);
160 | return 1;
161 | case '>':
162 | next = chunk.slice(0, 2);
163 | if(next == '>=') {
164 | tokens.push(['COMPARE', next, lineno]);
165 | return 2;
166 | } else if(next == '>>') {
167 | tokens.push(['MATH', next, lineno]);
168 | return 2;
169 | }
170 | tokens.push(['COMPARE', tag, lineno]);
171 | return 1;
172 | case '=':
173 | next = chunk.slice(0, 2);
174 | if(next == '==') {
175 | tokens.push(['COMPARE', next, lineno]);
176 | return 2;
177 | }
178 | tokens.push([tag, tag, lineno]);
179 | return 1;
180 | case '!':
181 | next = chunk.slice(0, 2);
182 | if(next == '!=') {
183 | tokens.push(['COMPARE', next, lineno]);
184 | return 2;
185 | }
186 | tokens.push([tag, tag, lineno]);
187 | return 1;
188 | case '*':
189 | case '/':
190 | case '%':
191 | tokens.push(['MATH', tag, lineno]);
192 | return 1;
193 | case '[':
194 | case '|':
195 | next = chunk.slice(0, 2);
196 | if(next == '||') {
197 | tokens.push(['BOOLOP', next, lineno]);
198 | return 2;
199 | }
200 | tokens.push([tag, tag, lineno]);
201 | return 1;
202 | case ')':
203 | if(tokens[tokens.length-1][0] == 'TERMINATOR') {
204 | tokens.pop();
205 | }
206 | tokens.push([tag, tag, lineno]);
207 | return 1;
208 | case '+':
209 | next = chunk.slice(0, 2);
210 | if(next == '++') {
211 | tokens.push(['CONCAT', tag, lineno]);
212 | return 2;
213 | }
214 | tokens.push([tag, tag, lineno]);
215 | return 1;
216 | case '-':
217 | next = chunk.slice(0, 2);
218 | if (next == '->') {
219 | tokens.push(['RIGHTARROW', next, lineno]);
220 | return 2;
221 | }
222 | tokens.push([tag, tag, lineno]);
223 | return 1;
224 | case '&':
225 | next = chunk.slice(0, 2);
226 | if(next == '&&') {
227 | tokens.push(['BOOLOP', next, lineno]);
228 | return 2;
229 | }
230 | return 0;
231 | case 'λ':
232 | case '\\':
233 | tokens.push(['LAMBDA', tag, lineno]);
234 | return 1;
235 | case '←':
236 | tokens.push(['LEFTARROW', tag, lineno]);
237 | return 1;
238 | case '→':
239 | tokens.push(['RIGHTARROW', tag, lineno]);
240 | return 1;
241 | case '⇒':
242 | tokens.push(['RIGHTFATARROW', tag, lineno]);
243 | return 1;
244 | case '@':
245 | case ']':
246 | case ':':
247 | case '.':
248 | case ',':
249 | case '{':
250 | case '}':
251 | case '(':
252 | tokens.push([tag, tag, lineno]);
253 | return 1;
254 | }
255 | return 0;
256 | };
257 |
258 | var shebangToken = function(chunk) {
259 | var token = SHEBANG.exec(chunk);
260 | if (token) {
261 | tokens.push(['SHEBANG', token[0], lineno]);
262 | return token[0].length;
263 | }
264 | return 0;
265 | };
266 |
267 | var tokenise = function(source, tokenizers) {
268 | /*jshint boss:true*/
269 | var i = 0, chunk;
270 |
271 | function getDiff(chunk) {
272 | return _.foldl(tokenizers, function(diff, tokenizer) {
273 | return diff ? diff : tokenizer.apply(tokenizer, [chunk]);
274 | }, 0);
275 | }
276 |
277 | while(chunk = source.slice(i)) {
278 | var diff = getDiff(chunk);
279 | if(!diff) {
280 | throw "Couldn't tokenise: " + chunk.substring(0, chunk.indexOf("\n") > -1 ? chunk.indexOf("\n") : chunk.length);
281 | }
282 | lineno += source.slice(i, i + diff).split('\n').length - 1;
283 | i += diff;
284 | }
285 |
286 | return tokens;
287 | };
288 |
289 | exports.tokenise = function(source) {
290 | indent = 0;
291 | indents = [];
292 | tokens = [];
293 | lineno = 0;
294 |
295 | return tokenise(source, [identifierToken, numberToken,
296 | stringToken, genericToken, commentToken, whitespaceToken,
297 | lineToken, literalToken, shebangToken]
298 | ).concat([['EOF', '', lineno]]);
299 | };
300 |
--------------------------------------------------------------------------------
/src/types.js:
--------------------------------------------------------------------------------
1 | var _ = require('underscore');
2 |
3 | // ### Prune
4 | //
5 | // This will unchain variables until it gets to a type or variable without an
6 | // instance. See `unify` for some details about type variable instances.
7 | var prune = function(type) {
8 | if(type instanceof Variable && type.instance) {
9 | type.instance = prune(type.instance);
10 | return type.instance;
11 | }
12 | return type;
13 | };
14 | exports.prune = prune;
15 |
16 | // ### Occurs check
17 | //
18 | // These functions check whether the type `t2` is equal to or contained within
19 | // the type `t1`. Used for checking recursive definitions in `unify` and
20 | // checking if a variable is non-generic in `fresh`.
21 | var occursInType = function(t1, t2) {
22 | t2 = prune(t2);
23 | if(t2 == t1) {
24 | return true;
25 | } else if(t2 instanceof ObjectType) {
26 | var types = [];
27 | for(var prop in t2.props) {
28 | types.push(t2.props[prop]);
29 | }
30 | return occursInTypeArray(t1, types);
31 | } else if(t2 instanceof BaseType) {
32 | return occursInTypeArray(t1, t2.types);
33 | }
34 | return false;
35 | };
36 | exports.occursInType = occursInType;
37 |
38 | var occursInTypeArray = function(t1, types) {
39 | return _.any(types, function(t2) {
40 | return occursInType(t1, t2);
41 | });
42 | };
43 |
44 | // ## Type variable
45 | //
46 | // A type variable represents an parameter with an unknown type or any
47 | // polymorphic type. For example:
48 | //
49 | // fun id x = x
50 | //
51 | // Here, `id` has the polymorphic type `#a -> #a`.
52 | var Variable = function(idString) {
53 | if(!idString) {
54 | this.id = Variable.nextId;
55 | Variable.nextId++;
56 | } else {
57 | this.id = variableFromString(idString);
58 | }
59 | this.instance = null;
60 | };
61 | Variable.nextId = 0;
62 | exports.Variable = Variable;
63 |
64 | // ### Fresh type
65 | //
66 | // Getting a "fresh" type will create a recursive copy. When a generic type
67 | // variable is encountered, a new variable is generated and substituted in.
68 | //
69 | // A fresh type is only returned when an identifier is found during analysis.
70 | // See `analyse` for some context.
71 | Variable.prototype.fresh = function(nonGeneric, mappings) {
72 | if(!mappings) mappings = {};
73 |
74 | var type = prune(this);
75 | if(!(type instanceof Variable)) {
76 | return type.fresh(nonGeneric, mappings);
77 | }
78 |
79 | if(occursInTypeArray(type, nonGeneric)) {
80 | return type;
81 | }
82 |
83 | if(!mappings[type.id]) {
84 | mappings[type.id] = new Variable();
85 | }
86 | return mappings[type.id];
87 | };
88 |
89 | var toChar = function(n) {
90 | return String.fromCharCode("a".charCodeAt(0) + n);
91 | };
92 | // Type variables should look like `'a`. If the variable has an instance, that
93 | // should be used for the string instead.
94 | //
95 | // This is just bijective base 26.
96 | var variableToString = function(n) {
97 | var a = '';
98 | if(n >= 26) {
99 | a = variableToString(n / 26 - 1);
100 | n = n % 26;
101 | }
102 | a += toChar(n);
103 | return a;
104 | };
105 | Variable.prototype.toString = function() {
106 | if(!this.instance) {
107 | return "#" + variableToString(this.id);
108 | }
109 | return this.instance.toString();
110 | };
111 |
112 | var variableFromString = function(vs) {
113 | return _.reduce(_.map(vs.split(''), function(v, k) {
114 | return v.charCodeAt(0) - 'a'.charCodeAt(0) + 26 * k;
115 | }), function(accum, n) {
116 | return accum + n;
117 | }, 0);
118 | };
119 |
120 | // ## Base type
121 | //
122 | // Base type for all specific types. Using this type as the prototype allows the
123 | // use of `instanceof` to detect a type variable or an actual type.
124 | var BaseType = function() {
125 | this.types = [];
126 | };
127 | BaseType.prototype.toString = function() {
128 | return this.name;
129 | };
130 | exports.BaseType = BaseType;
131 |
132 | // ## Specific types
133 | //
134 | // A `FunctionType` contains a `types` array. The last element represents the
135 | // return type. Each element before represents an argument type.
136 | var FunctionType = function(types, typeClasses) {
137 | this.types = types;
138 | this.typeClasses = typeClasses || [];
139 | };
140 | FunctionType.prototype = new BaseType();
141 | FunctionType.prototype.name = "Function";
142 | FunctionType.prototype.fresh = function(nonGeneric, mappings) {
143 | if(!mappings) mappings = {};
144 |
145 | var newTypeClasses = _.map(this.typeClasses, function(typeClass) {
146 | return typeClass.fresh(nonGeneric, mappings);
147 | });
148 |
149 | return new FunctionType(_.map(this.types, function(t) {
150 | return t.fresh(nonGeneric, mappings);
151 | }), newTypeClasses);
152 | };
153 | FunctionType.prototype.toString = function() {
154 | return this.name + "(" + _.map(this.types, function(type) {
155 | return type.toString();
156 | }).join(', ') + ")";
157 | };
158 | exports.FunctionType = FunctionType;
159 |
160 | var NumberType = function() {};
161 | NumberType.prototype = new BaseType();
162 | NumberType.prototype.fresh = function() {
163 | return this;
164 | };
165 | NumberType.prototype.name = "Number";
166 | exports.NumberType = NumberType;
167 |
168 | var StringType = function() {};
169 | StringType.prototype = new BaseType();
170 | StringType.prototype.fresh = function() {
171 | return this;
172 | };
173 | StringType.prototype.name = "String";
174 | exports.StringType = StringType;
175 |
176 | var BooleanType = function() {};
177 | BooleanType.prototype = new BaseType();
178 | BooleanType.prototype.fresh = function() {
179 | return this;
180 | };
181 | BooleanType.prototype.name = "Boolean";
182 | exports.BooleanType = BooleanType;
183 |
184 | var ArrayType = function(type) {
185 | this.type = type;
186 | this.types = [type];
187 | };
188 | ArrayType.prototype = new BaseType();
189 | ArrayType.prototype.name = "Array";
190 | ArrayType.prototype.fresh = function(nonGeneric, mappings) {
191 | if(!mappings) mappings = {};
192 | return new ArrayType(this.type.fresh(nonGeneric, mappings));
193 | };
194 | ArrayType.prototype.toString = function() {
195 | return '[' + this.type.toString() + ']';
196 | };
197 | exports.ArrayType = ArrayType;
198 |
199 | var ObjectType = function(props) {
200 | this.props = props;
201 | };
202 | ObjectType.prototype = new BaseType();
203 | ObjectType.prototype.name = "Object";
204 | ObjectType.prototype.fresh = function(nonGeneric, mappings) {
205 | var props = {};
206 | var name;
207 | for(name in this.props) {
208 | props[name] = this.props[name].fresh(nonGeneric, mappings);
209 | }
210 | var freshed = new ObjectType(props);
211 | if(this.aliased) freshed.aliased = this.aliased;
212 | return freshed;
213 | };
214 | ObjectType.prototype.getPropertyType = function(prop) {
215 | return this.props[prop];
216 | };
217 | ObjectType.prototype.toString = function() {
218 | var strs = [];
219 | var p;
220 | var n;
221 | var e;
222 | for(p in this.props) {
223 | if (_.isString(p)) {
224 | // Replace any double quotes by their escaped version.
225 | // Also replace any escaped single quotes.
226 | e = p.replace(/"|\\"/g, '\\"').replace(/(\\\\)|\\(')/g, '$1$2');
227 | // Normalize the string format to double quotes.
228 | n = e.replace(/^'(.*)'$|^\\"(.*)\\"$/, '"$1$2"');
229 | strs.push(n + ': ' + this.props[p].toString());
230 | } else {
231 | strs.push(p + ': ' + this.props[p].toString());
232 | }
233 | }
234 | return '{' + strs.join(', ') + '}';
235 | };
236 | exports.ObjectType = ObjectType;
237 |
238 | var TagNameType = function(name) {
239 | this.name = name;
240 | };
241 | TagNameType.prototype = new BaseType();
242 | TagNameType.prototype.fresh = function() {
243 | return new TagNameType(this.name);
244 | };
245 | exports.TagNameType = TagNameType;
246 |
247 | var TagType = function(types) {
248 | this.types = types;
249 | this.name = types[0].toString();
250 | };
251 | TagType.prototype = new BaseType();
252 | TagType.prototype.fresh = function(nonGeneric, mappings) {
253 | if(!mappings) mappings = {};
254 | return new TagType(_.map(this.types, function(t) {
255 | return t.fresh(nonGeneric, mappings);
256 | }));
257 | };
258 | TagType.prototype.toString = function() {
259 | return _.map(this.types, function(t) {
260 | return t.toString();
261 | }).join(' ');
262 | };
263 | exports.TagType = TagType;
264 |
265 | var UnitType = function() {};
266 | UnitType.prototype = new BaseType();
267 | UnitType.prototype.name = "Unit";
268 | UnitType.prototype.fresh = function() {
269 | return this;
270 | };
271 | exports.UnitType = UnitType;
272 |
273 | var NativeType = function() {};
274 | NativeType.prototype = new BaseType();
275 | NativeType.prototype.name = "Native";
276 | NativeType.prototype.fresh = function() {
277 | return this;
278 | };
279 | exports.NativeType = NativeType;
280 |
281 | var TypeClassType = function(name, type) {
282 | this.name = name;
283 | this.type = type;
284 | this.types = [type];
285 | };
286 | TypeClassType.prototype = new BaseType();
287 | TypeClassType.prototype.fresh = function(nonGeneric, mappings) {
288 | if(!mappings) mappings = {};
289 | return new TypeClassType(this.name, this.type.fresh(nonGeneric, mappings));
290 | };
291 | TypeClassType.prototype.toString = function() {
292 | return this.name + ' ' + this.type.toString();
293 | };
294 | exports.TypeClassType = TypeClassType;
295 |
--------------------------------------------------------------------------------
/site/index.htm:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Roy
5 |
6 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
82 |
83 |
84 |
85 |
86 |
87 |
Roy
88 |
89 |
90 |
91 |
About
92 |
Roy is an experimental programming language that targets JavaScript. It
93 | tries to meld JavaScript semantics with some features common in static
94 | functional languages: