Skip to content

Commit d7b7ed0

Browse files
remo5000martin-henz
authored andcommitted
Add Chapter 4 (#23)
* Add specified function definitions Misc functions are to be imported after this * Import type checking and error methods Only some are used, but all of them are imported to keep this file up to date with all the functions that were present before * Remove duplicate error function * Add metaCircularParser to Context This Parser will be preserved for the lifetime of the Context, and this ensures that it is reset together with the context as well. * Add parse function * Fix no typing error for misc * Rename JSON.stringify -> stringify * Remove unused Parser import From rebasing away jison commits * Add base parser file * Format parser file * Use .js for parser file * Import and use parser for parse function * Add parser jison source * Add metacircular interpreter source * Add doc to refer to source * Fix tests not compiling There was a problem with transpiling the files when testing, because of the .js files used. I first added a transformer for the .js files (that didnt have one at first, throwing an error at the `export` line in parser.js). Then, I added the presets required, which were displayed as missing presets when trying to test * Update build to a script This script will run the typescript build, and then copy over the required .js file over. Note that to add more .js files, the build script will need to be modified. * Use && for failing tsc * testing dev env * Add .js files to interpreter folder Oh my * testing of martins dev setup for meta * another try * another try: getting really ugly * getting uglier * getting ready for prime time * forgot removing tag * trying to get => to work * another attempt to fix arrow * minor mods
1 parent 462b050 commit d7b7ed0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+13786
-9
lines changed

package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
],
2323
"scripts": {
2424
"prepublishOnly": "tsc",
25-
"build": "tsc",
25+
"build": "scripts/build.sh",
2626
"test": "jest",
2727
"test-coveralls": "jest --coverage --coverageReporters=text-lcov | coveralls"
2828
},
@@ -39,6 +39,8 @@
3939
"@types/invariant": "^2.2.29",
4040
"@types/jest": "^23.1.4",
4141
"@types/node": "^9.4.7",
42+
"babel-preset-es2015": "^6.24.1",
43+
"babel-preset-stage-2": "^6.24.1",
4244
"coveralls": "^3.0.1",
4345
"jest": "^23.3.0",
4446
"ts-jest": "^23.0.0",
@@ -50,7 +52,8 @@
5052
"js"
5153
],
5254
"transform": {
53-
"\\.ts$": "<rootDir>/node_modules/ts-jest/preprocessor.js"
55+
"\\.ts$": "<rootDir>/node_modules/ts-jest/preprocessor.js",
56+
"\\.js$": "<rootDir>/node_modules/babel-jest"
5457
},
5558
"testRegex": "/__tests__/.*\\.ts$",
5659
"testPathIgnorePatterns": [

scripts/build.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Compile all .ts files, then
2+
# copy the only .js file, parser.js, to dist.
3+
# This is required because it is not possible to both inlcude .js
4+
# files (allowJs = true) and export type declarations (declarations = true)
5+
# in tsconfig. See https://github.com/Microsoft/TypeScript/issues/7546
6+
tsc && cp src/stdlib/parser.js dist/stdlib

src/createContext.ts

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ import * as misc from './stdlib/misc'
33
import { Context, CustomBuiltIns, Value } from './types'
44
import { toString } from '.';
55

6+
/** Import meta-circular parser */
7+
const createParserModule = require('./stdlib/parser')
8+
const createParser = createParserModule.default
9+
610
const GLOBAL = typeof window === 'undefined' ? global : window
711

812
const createEmptyCFG = () => ({
@@ -24,7 +28,8 @@ export const createEmptyContext = <T>(chapter: number, externalSymbols: string[]
2428
errors: [],
2529
externalContext,
2630
cfg: createEmptyCFG(),
27-
runtime: createEmptyRuntime()
31+
runtime: createEmptyRuntime(),
32+
metaCircularParser: createParser()
2833
})
2934

3035
export const ensureGlobalEnvironmentExist = (context: Context) => {
@@ -132,6 +137,34 @@ export const importBuiltins = (context: Context, externalBuiltIns: CustomBuiltIn
132137
defineSymbol(context, 'array_length', misc.array_length)
133138
}
134139

140+
if (context.chapter >= 4) {
141+
defineSymbol(context, 'stringify', JSON.stringify)
142+
defineSymbol(context, 'parse',
143+
function () {
144+
return context.metaCircularParser
145+
.parse.apply(context.metaCircularParser, arguments)
146+
});
147+
defineSymbol(context, 'apply_in_underlying_javascript', function(
148+
fun: Function,
149+
args: Value
150+
) {
151+
const res = []
152+
var i = 0
153+
while (!(args.length === 0)) {
154+
res[i] = args[0]
155+
i = i + 1
156+
args = args[1]
157+
}
158+
return fun.apply(fun, res)
159+
})
160+
defineSymbol(context, 'is_number', misc.is_number)
161+
defineSymbol(context, 'is_array', misc.is_array)
162+
defineSymbol(context, 'is_object', misc.is_object)
163+
defineSymbol(context, 'is_string', misc.is_string)
164+
defineSymbol(context, 'is_function', misc.is_function)
165+
defineSymbol(context, 'is_boolean', misc.is_boolean)
166+
}
167+
135168
if (context.chapter >= Infinity) {
136169
// previously week 4
137170
defineSymbol(context, 'alert', alert)
@@ -140,7 +173,6 @@ export const importBuiltins = (context: Context, externalBuiltIns: CustomBuiltIn
140173
// previously week 5
141174
defineSymbol(context, 'assoc', list.assoc)
142175
// previously week 6
143-
defineSymbol(context, 'is_number', misc.is_number)
144176
}
145177
}
146178

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
INTERPRETER_CORE = lib/exports.js.tpl \
2+
lib/interpreter/interpreter.js \
3+
lib/interpreter/json2.js \
4+
lib/interpreter/parser.jison.tpl \
5+
lib/util/io.js
6+
7+
PARSER = lib/interpreter/parser.js
8+
9+
.PHONY: environment academy
10+
11+
all: environment
12+
13+
# removed from dependencies for environment: ../list.js ../object.js ../stream.js
14+
environment: lib/interpreter/parser.js
15+
16+
clean:
17+
rm -f jediscript-*.js
18+
19+
distclean: clean
20+
rm -f lib/*.js
21+
22+
academy: environment
23+
node generate.js --interpreter 5 --without_jquery
24+
node generate.js --interpreter 8 --without_jquery
25+
node generate.js --interpreter 13 --without_jquery --debug
26+
27+
offline_zip: environment
28+
node generate.js --interpreter 5
29+
node generate.js --interpreter 8
30+
node generate.js --interpreter 13
31+
32+
$(PARSER): $(INTERPRETER_CORE)
33+
node generate.js --environment
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# JediScript Meta-Circular Evaluator
2+
####_vastly more up-to-date instructions, in nice Markdown format._
3+
4+
This project comprises 4 main components:
5+
6+
1. The parser, written in Jison
7+
2. The interpreter, written in legal JediScript
8+
3. The scratch project templates, which are a combination of JediScript, JavaScript, and Markup-JS (a templating engine)
9+
4. The project generator (`generate.js`)
10+
11+
These all work together to form the functional evaluator.
12+
13+
## Setting up the environment
14+
1. This project depends on node.js. Install [node.js](http://nodejs.org) for your platform.
15+
2. Using the shell for your platform, run `envsetup.{sh|bat}`. This will obtain the necessary installation dependencies and generate the development versions of the templates.
16+
- Note that your Jison install will be patched to throw SyntaxError exceptions when parse errors occur. This may affect your other Jison dependents, if any.
17+
2 (alternative) . Just run `npm install` and all the dependencies should be magically installed.
18+
3. If you have made changes to the templates, generate new development libraries by running `generate.js --environment`.
19+
4. `scratch.html` in the root directory of the project has got the evaluator-in-evaluator code being part of `test3`. Use that to test the interpreter interpreting itself.
20+
21+
## Generating Student Scratch Projects
22+
In addition to generating the development libraries, the templates can be used in concert with the generator to produce scratch projects which only contain valid JediScript elements for any given week. Generate these by running `generate.js --week N`.
23+
24+
If you are modifying the templates, note that:
25+
26+
- When modifying the Jison template source, Jison blocks within Markup-JS blocks (i.e. Jison declarations within the `{{if}}` or other blocks) must have `$$` escaped to `$$$`. This is because Markup-JS will remove one `$`, leaving invalid Jison declarations
27+
- If new user-defined functions must be provided to the student, update `export.js`. `export.js` contains a list of key-value pairs, which will be registered in the interpreter so the student can access them from his script file.
28+
- By default, the generated scratch project will have the parser screen the input from the student's script tags and evaluate it. The student's code can still be directly evaluated by the browser's JavaScript engine by passing `--native` (`--no-native` will equivalently enable interpreted mode. But that's the default)
29+
- The generator will send all output through to Google's Closure compiler, both to shrink the codebase and also to optimise the code. Passing `--debug` while generating the package will suppress this, so that the generator (or the libraries) can be debugged.
30+
31+
## Unit Testing
32+
I've coded unit tests for the interpreter. Open `test.html`, the test suite will run automatically. This uses the parser and other libraries generated by `generate.js --environment`, so be sure to run it before any testing.
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
2+
var jison = require("./lib/interpreter/parser-week-13");
3+
4+
function parse(text) {
5+
return new jison.Parser().parse(text);
6+
}
7+
8+
function findSource(text, start_line, start_col, end_line, end_col) {
9+
var lines = text.replace(/\r/g, '').split('\n');
10+
11+
--start_line, --end_line; // 0-based indexing for lines
12+
// cols are already 0-based
13+
14+
var range = [];
15+
for (var i=start_line; i<=end_line; i++) {
16+
range.push(lines[i]);
17+
}
18+
19+
if (range.length === 1) {
20+
range[0] = range[0].slice(start_col, end_col+1);
21+
return range[0];
22+
} else {
23+
range[0] = range[0].slice(start_col);
24+
range[range.length-1] = range[range.length-1].slice(0, end_col+1);
25+
return range.join('\n');
26+
}
27+
}
28+
29+
function listToArray(xs) {
30+
var result = [];
31+
while (xs.length !== 0) {
32+
result.push(xs[0]);
33+
xs = xs[1];
34+
}
35+
return result;
36+
}
37+
38+
function findTopLevelFunctionDeclarations(text) {
39+
var ast = listToArray(parse(text));
40+
var declarations = ast.filter(function (node) {
41+
return node.tag === 'var_definition'
42+
&& node.value
43+
&& node.value.tag === 'function_definition';
44+
}).map(function (node) {
45+
var loc = node.value.location;
46+
return {
47+
name: node.variable,
48+
source: findSource(text, loc.start_line, loc.start_col, loc.end_line, loc.end_col)
49+
};
50+
});
51+
return declarations;
52+
}
53+
54+
// Invert symbol object so we can easily lookup symbol names
55+
56+
var symbols = new jison.Parser().symbols_;
57+
var symbolName = {};
58+
Object.keys(symbols).forEach(function (key) {
59+
symbolName[symbols[key]] = key;
60+
});
61+
62+
function lex(input) {
63+
var lexer = new jison.Parser().lexer;
64+
lexer.setInput(input);
65+
var token;
66+
var tokens = [];
67+
while (symbolName[token = lexer.lex()] !== 'EOF') {
68+
tokens.push(symbolName[token]);
69+
}
70+
return tokens;
71+
}
72+
73+
module.exports = {
74+
count: function(program) {
75+
var declarations = findTopLevelFunctionDeclarations(program);
76+
declarations.forEach(function (item) {
77+
item.count = lex(item.source).length;
78+
});
79+
return declarations;
80+
}
81+
};
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
var fs = require('fs');
3+
var count = require('./count-tokens').count;
4+
5+
var input = "function x()\n{return 1;}\nfunction y()\n{return 1;}\nfunction z()\n{return 10;}";
6+
7+
console.log(count(input));
8+
console.log(count(fs.readFileSync('shouldbe36.js', 'utf8')));

0 commit comments

Comments
 (0)