You can help with this project by
- Fixing bugs (see issues for bugs)
- Implementing a library (please post it as an issue first so others know you are working on it)
- Adding/fixing test cases
- Introducing new features
- Discussing and sharing your thoughts (in issues)
If you want to leave your footprints in this project, you can
- Fork it
git clonethennpm install- Leave
distandlibalone, because they are automatically maintained - (optional)
grunt# default task of grunt is to build and watch - Make your contribution
npm run buildnpm run test- Commit, push then send a pull request here.
Use npm run build to update files in dist and lib folders.
Use npm run test to make sure that you did not break anything.
The syntax parser is generated by PEG.js. Put your .pegjs file in pegjs/ folder, then use grunt peg:build to generate your syntax parser (or just use grunt build). The parsers will be created in lib/ folder under the same name.
The project is written in CoffeeScript. And you are encouraged to use it as well. It has a python-like syntax that utilizes forced indents to "free" people from brackets and semicolons. I think CoffeeScript code is generally more readable.
Any PR is welcome.
To make it easier to run cpp files and tests, you can use
bin\run <test name>
bin\debug <test name>
bin\run -f <file path> -i <stdio input>
bin\debug -f <file path> -i <stdio input>
for example
# run "function_pointer" test case with input described in test/test.yaml
bin\run function_pointer
# debug JSCPP with node-inspector on "function_pointer" test case
bin\debug function_pointer
# run/debug "test\function_pointer.cpp" with customized input
bin\run -f test\function_pointer.cpp -i "10 20"
bin\debug -f test\function_pointer.cpp -i "10 20"
We need to enable harmony flag to run node-debug. For example, use the following command to debug JSCPP with test/8.bit.cpp as input.
node-debug --nodejs --harmony demo/debug 8bit
This part contains detailed remarks on this project.
If you find the bug, please post it to issues first before trying to fix. Because it may just be a misuse.
There are 4 phases during an interpretation. Namely, preprocessing syntax parsing, preprocessing handling, syntax parsing, interpretation. And they handled by different modules.
If the bug was not covered by existing test cases, you are highly encouraged to add the test cases for it. See "On adding/fixing test cases" subsection for details.
All libraries are located in src\includes folder. Each library should export an object with load method. You can also use rt.load("dependency") to load other libraries. Every library will only be loaded once.
Don't forget to add your library into launcher.js.
Each test can have many source files and many input cases. First you need to put your C++ source file in test\ folder. Then you add the test description in test\test.yaml. Each test description reads like this:
A+B:
after:
- "cincout"
cases:
cpp: "A+B.cpp"
in: "10 506"
out: "516"Each test description has a "after" field that requires this test to be run after the designated tests. When all prerequisite tests have passes, the test will start immediately. Each source file in "cpp" field will be tested against each input/output case. Not passing a "in" or "out" field will have the same result as passing an empty string.
Sometime you need to test for exceptions. You can use "exception" field in a test case:
8bit:
after:
- "A+B"
cases:
-
cpp: "8bit2.cpp"
out: "127\n"
-
cpp: "8bit.cpp"
exception: "6:3 overflow during post-increment 256\\(unsigned char\\)"The exception is tested as a regex rather than plain string matching.
You should open and issue for enhancement first. Other people can probably give some hints.
Just open an issue for discussion.
Here describes some detailed information.
The inner structure of value of each type.
Primitive types
bool
- t:
- type: "primitive"
- name: "bool"
- v: {1 or 0}
char
- t:
- type: "primitive"
- name: "char"
- v: {char code}
other
- t:
- type: "primitive"
- name: {type name}
- v: {type value}
Pointer types
Normal pointer
- t:
- type: "pointer"
- ptrType: "normal"
- targetType: {type of target}
- v:
- target: {target}
A Void pointer is a Normal pointer with its ptrType set to "void".
Array pointer
- t:
- type: "pointer"
- ptrType: "array"
- eleType: {type of element}
- size: {size of array}
- v:
- target: [{type-values}]
- position: {offset}
Function
- t:
- type: "function"
- retType: {return type}
- signature: {signature(rt.makeParametersSigniture)}
- v:
- target: {f(rt, _this, args...){}}
- name: {name}
- defineType: {class type}
- args: {signature(rt.makeParametersSigniture)}
- retType: {return type}
Class types
- t:
- type: "class"
- name: {class name}
- v:
- members: {a <name, value> dictionary}
All functions are registered in rt.types (rt is an instance of CRuntime). Every function can be called like this:
rt.types[{type signature}][{function name}][{function signature}](rt, _this, args...)
Type signature is the signature of the type that the function is defined upon. It can be retrieved with rt.getTypeSignature. When it is a global function, the type signature is "global".
Funtion signature contains the information on the return type and parameter types of a function. It can be retrieved with rt.getTypeSignature.
Operators behave exactly like functions, except that their signatures should be retrieved with rt.makeOperatorFuncName.
There are some special cases of function names:
Class
- #constructor: {f(rt, _this)}
- #members:
- name: name of the property
- initialize (optional): {f(rt, _this)}, initializer function for this member
There are some special cases of function signature
Function
- reg: this is a dictionary <function signiture, function type> that are declared under this name of this type.
Function and operator
- #default: {f(rt, _this, args...)} this gets called (if exists) when no other signature matches the actual parameters. Useful for variable length functions and parameters having uncertain types.