GithubHelp home page GithubHelp logo

felixhao28 / jscpp Goto Github PK

View Code? Open in Web Editor NEW
856.0 28.0 72.0 7.92 MB

A simple C++ interpreter written in JavaScript

Home Page: https://felixhao28.github.io/JSCPP/

License: MIT License

JavaScript 4.98% C++ 6.70% Batchfile 0.03% TypeScript 88.29%

jscpp's Introduction

JSCPP

This is a simple C++ interpreter written in JavaScript.

Try it out on github.io!

Travis Build Status npm version Dependency Status devDependency Status

Purpose of the project

As far as I know, every public online C++ excuting environment requires backend servers to compile and run the produced executable. A portable and lightweight interpreter that can be run in browsers can be a fine substitute for those who do not intend to pay for such services.

I also want to make a strict interpreter. The reason being C++ has too many undefined and platform-dependent behaviors and popular C++ compilers tend to be an "over-caring mother" who tries to ignore or even justify the undocumented usages. The abuse of them should be avoided as much as possible IMO. For example, I do not want my students to take it as guaranteed that sizeof int produces 4, because on Arduino Uno, an int is a 2-byte value.

Currently, it is mainly for educational uses for a MOOC course I am running (and fun).

Prerequisites

  • NodeJS version >= 0.11
  • A modern browser

How to use

Installation

npm install JSCPP

or (to use lastest cutting-edge version or to contribute)

git clone https://github.com/felixhao28/JSCPP.git
cd JSCPP
npm install .

Or you can download the minified single JS file directly from here:

https://raw.githubusercontent.com/felixhao28/JSCPP/gh-pages/dist/JSCPP.es5.min.js

With NodeJS

var JSCPP = require("JSCPP");
var code =    "#include <iostream>"
            + "using namespace std;"
            + "int main() {"
            + "    int a;"
            + "    cin >> a;"
            + "    cout << a << endl;"
            + "    return 0;"
            + "}"
;
var input = "4321";
var exitcode = JSCPP.run(code, input);
console.info("program exited with code " + exitcode);

See demo/example.coffee for example.

Main API: JSCPP.run(code, input, config):

  • code: string The C++ source code to be interpreted.
  • input: string The text to be sent into standard input (can be overriden with config.stdio).
  • config: <optional> JSCPPConfig The configuration object. All configuration items have default value. So you only need to set the ones you want different from the defaults.
    • specifiers: <optional> string[]

      • Allowed specifiers. By default all specifiers are allowed.
      • Default: ["const", "inline", "_stdcall", "extern", "static", "auto", "register"]
    • charTypes: <optional> string[]

      • Allowed char types. By default all char types are allowed.
      • Default: ["char", "signed char", "unsigned char", "wchar_t", "unsigned wchar_t", "char16_t", "unsigned char16_t", "char32_t", "unsigned char32_t"]
    • intTypes: <optional> string[]

      • Allowed integer types. By default all integer types are allowed.
      • Default: ["short", "short int", "signed short", "signed short int", "unsigned short", "unsigned short int", "int", "signed int", "unsigned", "unsigned int", "long", "long int", "long int", "signed long", "signed long int", "unsigned long", "unsigned long int", "long long", "long long int", "long long int", "signed long long", "signed long long int", "unsigned long long", "unsigned long long int", "bool"]
    • limits: <optional> {[type: string]: { max: number, min: number, bytes: number}}

      • The minimal and the maximum value on number types. You can just set a subset of all the types, and the unset types will use the default limits.
      • Default:
      {
          "char": {
              max: 0x7f,
              min: 0x00,
              bytes: 1
          },
          "signed char": {
              max: 0x7f,
              min: -0x80,
              bytes: 1
          },
          "unsigned char": {
              max: 0xff,
              min: 0x00,
              bytes: 1
          },
          "wchar_t": {
              max: 0x7fffffff,
              min: -0x80000000,
              bytes: 4
          },
          "unsigned wchar_t": {
              max: 0xffffffff,
              min: 0x00000000,
              bytes: 4
          },
          "char16_t": {
              max: 0x7fff,
              min: -0x8000,
              bytes: 4
          },
          "unsigned char16_t": {
              max: 0xffff,
              min: 0x0000,
              bytes: 4
          },
          "char32_t": {
              max: 0x7fffffff,
              min: -0x80000000,
              bytes: 4
          },
          "unsigned char32_t": {
              max: 0xffffffff,
              min: 0x00000000,
              bytes: 4
          },
          "short": {
              max: 0x7fff,
              min: -0x8000,
              bytes: 2
          },
          "unsigned short": {
              max: 0xffff,
              min: 0x0000,
              bytes: 2
          },
          "int": {
              max: 0x7fffffff,
              min: -0x80000000,
              bytes: 4
          },
          "unsigned": {
              max: 0xffffffff,
              min: 0x00000000,
              bytes: 4
          },
          "long": {
              max: 0x7fffffff,
              min: -0x80000000,
              bytes: 4
          },
          "unsigned long": {
              max: 0xffffffff,
              min: 0x00000000,
              bytes: 4
          },
          "long long": {
              max: 0x7fffffffffffffff,
              min: -0x8000000000000000,
              bytes: 8
          },
          "unsigned long long": {
              max: 0xffffffffffffffff,
              min: 0x0000000000000000,
              bytes: 8
          },
          "float": {
              max: 3.40282346638529e+038,
              min: -3.40282346638529e+038,
              bytes: 4
          },
          "double": {
              max: 1.79769313486232e+308,
              min: -1.79769313486232e+308,
              bytes: 8
          },
          "pointer": {
              max: undefined,
              min: undefined,
              bytes: 4
          },
          "bool": {
              max: 1,
              min: 0,
              bytes: 1
          }
      }
    • includes: <optional> { [fileName: string]: IncludeModule }

      • Define additional include files. This is extremely useful if you are defining new types, variables or functions to be used in the C++ source code.
      • IncludeModule is an object that has a load(rt: CRuntime): void member function. For example,
        {
          "myheader.h": {
            load: function(rt) {
              rt.regFunc(function(rt, _this, x, y) {
                var firstValue = x.v;
                var secondValue = y.v;
                var returnType = x.t;
                return rt.val(returnType, firstValue + secondValue);
              }, "global", "myfunction", [rt.intTypeLiteral, rt.intTypeLiteral], rt.intTypeLiteral);
            }
          }
        }
        will register a global function equivalent to the following, before interpreting the source code:
        // C++ code
        int myfunction(int x, int y) {
          return x + y;
        }
        so that user C++ code like this can be interpreted:
        // C++ code
        #include "myheader.h"
        int main() {
          return myfunction(1, 2); // will return 3
        }
        For more examples on writing a custom IncludeModule, including how to properly use types, values and variables, please take a look at the files inside src/includes. For custom classes (experimental), please take a look at src/includes/dummy_class_foo.ts and test/class_basics.cpp.
    • loadedLibraries: <optional> string[]

      • loadedLibraries keeps track of loaded headers. It can also be used to skip loading certain headers if given initial value.
    • stdio: <optional if in NodeJS> string[] { drain?: () => string; write: (s: string) => void; }

      • This controls the behavior of standard input/output. This is required if you are running JSCPP on webpages, since the default behavior of writing to standard output stream is to print to the console, which is invisible to end users.
      • drain: <optional> () => string
        • Executed whenever the standard input buffer needs new content. The returned string will be concatenated to the existing buffer. If drain is set, drain will be favored over input. This is useful if the standard input is extremely large or is not immediately available at the start but only available later during the interpretation, for example, debugging. You don't normally need to set drain.
      • write: (s: string) => void
        • Write the string s to standard output stream. By default it is implemeted as (s) => process.stdout.write(s);. You need to override this if you want to capture the console output and do something with it.
    • unsigned_overflow: <optional> "error" (default) | "warn" | "ignore"

      • Overflowing an unsigned type is an undefined behavior. This configuration controls what to do if a such overflow happens.
        • "error": immediately throw an exception.
        • "warn": print a warning to standard error stream.
        • "ignore": ignore the overflow and carry on interpreting.
    • maxTimeout: <optional> number

      • If set, JSCPP will throw an exception if the milliseconds since the beginnig of execution exceeds maxTimeout. This is not used in debug mode.
    • debug: <optional> boolean

      • If false (default), JSCPP will run normally and the return value of JSCPP.run will be the exit code of the C++ program.
      • If true, JSCPP will enter debug mode, break on the first AST node and an debugger instance will be immediately returned instead. Please refer to the "Using debugger" part of this document for further details.

Using debugger

As of 2.0.0, there is a simple but functional real debugger available.

A list of debugger API:

  • methods
    • debugger.next(): one step further
    • debugger.continue(): continue until breakpoint
    • debugger.nextNode(): the AST node to be executed
      • sLine
      • sColumn
      • sOffset
      • eLine
      • eColumn
      • eOffset
    • debugger.nextLine()
    • debugger.type(typeName)
    • debugger.variable()
    • debugger.variable(variableName)
  • properties
    • src: preprocessed source
    • prevNode: previous AST node
    • done
    • conditions
    • stopConditions
    • rt: the internal runtime instance
    • gen: the internal generator
var JSCPP = require("JSCPP")
var mydebugger = JSCPP.run(code, input, { debug: true });
// continue to the next interpreting operation
var done = mydebugger.next();
// if you have an active breakpoint condition, you can just continue
var done = mydebugger.continue();
// by default, debugger pauses at every new line, but you can change it
mydebugger.setStopConditions({
    isStatement: true
    positionChanged: true
    lineChanged: false
});
// so that debugger only stops at a statement of a new position
// or you can add your own condition, i.e. stops at line 10
mydebugger.setCondition("line10", function (prevNode, nextNode) {
	if (nextNode.sLine === 10) {
		// disable itself so that it only triggers once on line 10
		mydebugger.disableCondition("line10");
		return true;
	} else {
		return false;
	}
});
// then enable it
mydebugger.enableCondition("line10");
// we need to explicitly use "false" because exit code can be 0
if (done !== false) {
	console.log("program exited with code " + done.v);
}
// the AST node to be executed next
var s = mydebugger.nextNode();
// sometimes a breakpoint can be set without a statement to be executed next,
// i.e. entering a function call.
while ((s = mydebugger.nextNode()) == null) {
	mydebugger.next();
}
// the content of the statement to be executed next
var nextLine = mydebugger.nextLine();
// it is essentially same as
nextLine = mydebugger.getSource().slice(s.sOffset, s.eOffset).trim()

console.log("from " + s.sLine + ":" + s.sColumn + "(" + s.sOffset + ")");
console.log("to " + s.eLine + ":" + s.eColumn + "(" + s.eOffset + ")");
console.log("==> " + nextLine);
// examine the internal registry for a type
mydebugger.type("int");
// examine the value of variable "a"
mydebugger.variable("a");
// or list all local variables
mydebugger.variable();

A full interactive example is available in demo/debug.coffee. Use node -harmony demo/debug A+B -debug to debug "A+B" test.

With a browser

There should be a newest version of JSCPP.js or JSCPP.es5.js in dist ready for you. If not, use npm run build to generate one.

Then you can add it to your html. The exported global name for this package is "JSCPP".

<script src="JSCPP.es5.min.js"></script>
<script type="text/javascript">
	var code = 	"#include <iostream>"+
				"using namespace std;"+
				"int main() {"+
				"    int a;"+
				"    cin >> a;"+
				"    cout << a << endl;"+
				"    return 0;"+
				"}"
	;
	var input = "4321";
	var output = "";
	var config = {
		stdio: {
			write: function(s) {
				output += s;
			}
		},
		unsigned_overflow: "error" // can be "error"(default), "warn" or "ignore"
	};
	var exitCode = JSCPP.run(code, input, config);
	alert(output + "\nprogram exited with code " + exitCode);
</script>

If you do not provide a customized write method for stdio configuration, console output will not be correctly shown. See demo/demo.html for example.

Running in WebWorker

There are two Helper classes to make JSCPP easier to run in WebWorkers. One is JSCPP.WebWorkerHelper in an old callback style and JSCPP.AsyncWebWorkerHelper in a modern Promise/async-await style.

<script src="JSCPP.es5.min.js"></script>
<script type="text/javascript">
	var helper = new JSCPP.WebWorkerHelper("./JSCPP.es5.min.js"); // it is a class
	var output = "";
	helper.run(`#include <iostream>
		using namespace std;
		int main() {
		int a;
		cin >> a;
		a += 7;
		cout << a*10 << endl;
		return 0;
	}`, "5", {
		stdio: {
			write: function(s) {
				output += s;
			}
		}
	}, function (err, returnCode) {
		if (err) {
			alert("An error occurred: " + (err.message || err));
		} else {
			alert("Program exited with code " + returnCode);
		}
	});

	helper.worker.terminate(); // directly control the Worker instance
</script>
<script src="JSCPP.es5.min.js"></script>
<script type="text/javascript">
	async function asyncWrapper() {
		var helper = new JSCPP.AsyncWebWorkerHelper("./JSCPP.es5.min.js"); // it is a class
		var output = "";
		try {
			var returnCode = await helper.run(`#include <iostream>
				using namespace std;
				int main() {
				int a;
				cin >> a;
				a += 7;
				cout << a*10 << endl;
				return 0;
			}`, "5", {
				stdio: {
					write: function(s) {
						output += s;
					}
				}
			});
			alert("Program exited with code " + returnCode);
		} catch (err) {
			alert("An error occurred: " + (err.message || err));
		}
		helper.worker.terminate(); // directly control the Worker instance
	}
	asyncWrapper();
</script>

The helper classes are implemented in src/index.js, and a test page is available in dist/index.html.

Run tests

npm run test

Q&A

Which features are implemented?

  • (Most) operators
  • Primitive types
  • Variables
  • Arrays
    • Multidimensional array with initializers.
  • Pointers
  • If...else control flow
  • Switch...case control flow
    • Declarations inside switch block.
  • For loop
  • While loop
  • Do...while loop
  • Functions
  • Variable scopes
  • Preprocessor directives
    • Macro
    • Include

Which notable features are not implemented yet?

  • Goto statements
  • Object-oriented features
  • Namespaces
  • Multiple files support

How is the performance?

If you want to run C++ programs effciently, compile your C++ code to LLVM-bitcode and then use Emscripten.

Which libraries are supported?

See current progress in includes folder.

  • iostream (only cin and cout and endl)
  • cmath
  • cctype
  • cstring
  • cstdio (partial)
  • cstdlib (partial)

Bug report? Feedback?

Post it on Issues.

Changelog

  • v2.0.10

    • New
      • Added experimental support for passing a function pointer as parameter.
    • Fix
      • Fixed strcmp comparing two identical strings.
  • v2.0.9 (2021.1.27)

    • New
      • WebWorker support (check dist/index.html for examples).
    • Fix
      • Fixed an error message
  • v2.0.7

    • New
      • Support some basic class usages (see test\class_basics.cpp).
      • Uses special uninitialized numeric and poitner value.
      • Adds an "unsigned_overflow" option to treat unsigned numeric overflow as "error"(default), "warn" or "ignore". Overflow on signed types will always throw an error.
    • Fix
      • Fixed some string-related methods.
      • Fixed pow in <cmath>, thanks to Clemenard.
      • Fixed multi-dimensional array declaration with initializers, thanks to rodrigorgs.
  • v2.0.6 (9.29)

    • New
      • Implemented following methods from cstdlib, thanks to victorrseloy:
        • int getchar(void)
        • char *gets(char *str)
        • int putchar(int char)
        • int puts(const char *str)
        • int scanf(const char *format, ...)
        • int sscanf(const char *str, const char *format, ...)
    • Fix
      • Fixed printf function
    • Dev
      • Update dependencies to latest
  • v2.0.5 (4.6)

    • New
      • ctime library
    • Fix
      • Supports functions returning pointer type
  • v2.0.4 (12.10)

    • New
      • Function default arguments support
    • Fix
      • cin.get now works as expected
    • Dev
      • Update dev dependencies
  • v2.0.3 (10.15)

    • New
      • Function pointer support
      • typedef support
    • Dev
      • Testing now uses test/test.coffee directly
      • Update PEG.js from 0.8.0 to 0.9.0
      • Please use NodeJS v4.0.0+
  • v2.0.2 (7.31)

    • New
      • Wide char support (русский язык)
    • Fix
      • Truncating negative number to positive
      • Error message when overflow
      • Error when using function prototypes
    • Dev
      • test.json -> test.yaml
      • Transpile to es5 properly (dist/JSCPP.es5.js)
      • Minified version (dist/JSCPP.es5.min.js)
      • Online site uses minified es5 version now
      • A few dependencies have been updated
      • Added linux helper for running and debugging (bin/run & bin/debug)
  • v2.0.1 (6.24)

    • Fix
      • Debugger variable scope issue
      • Readme example
      • An issue on Chrome Canary
      • Integer type promotion issue
      • Many small fixes
  • v2.0.0 (4.11)

    • New
      • Real debugger!
    • Change
      • API: Now JSCPP.run is all you need
      • Runtime: The project uses es6, please use V8 with harmony flag
      • Deprecated: Removed old legacy profiling-replay debugging
      • Misc: Many other small changes
    • Dev
      • Major refactoring on interpreter using es6
  • v1.1.1 (4.3)

    • Split debugger from example
    • (dev-side) Grunt only watches newer
    • Fix debug prev command
  • v1.1.0 (4.2)

    • Fixed array initialization with 0 issue
    • Added support for reading string with cin
    • Member function should not be registered globally
    • Added new tests
    • Basic debugging support
  • v1.0.3 (4.1)

    • (dev-side) Fix dev-dependency on coffee-script.
    • (dev-side) Grunt watches.
    • (dev-side) Port to coffeescript
    • (dev-side) Refactoring
    • (dev-side) Reworked testing, now all tests are defined in test.json
    • Fixed a bug related to a.push(b).concat(c) in syntax parser (#1).
    • Added new tests
  • v1.0.2 (3.31)

    • New examples.
    • Update README.
    • Renamed runtime and interpreter to start with upper case.
    • (dev-side) Grunt.
  • v1.0.1 (3.31)

    • This release is a mistake.
  • v1.0.0 (2015.3.31)

    • Formal release of this project.

jscpp's People

Contributors

alexandruionascu avatar dependabot-preview[bot] avatar dependabot[bot] avatar felixfritz avatar felixhao28 avatar fo-nttax avatar milahu avatar nickkolok avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jscpp's Issues

Generate x86 binary code

I'm looking for a C/C++ compiler written in Javascript capable of generate Linux x86 .so files, something similar to LLVM.js + clangor and ccjs (unluckily all of them abandoned :-( ). My intention is to generate Node.js compiled modules from inside NodeOS. Do you think JsCpp is capable of doing this, or do you know of any other alternative?

Cyrillic text is not supported

Greetings from Russia, comrads!

Thanks a lot for your work, it seems to be very useful for partial replacing MS Visual Studio in studying (at least for basics). Unfortunately, our students are useful to make cyrillic annotations for output... which don't work. Here is MWE:

#include <iostream>

using namespace std;

int main() {
    cout << "some cyrillic text: кириллический текст" <<  endl;
    return 0;
}

It seems like I can't send non-ASCII characters to cout:

#include <iostream>

using namespace std;

int main() {
    cout << "\u0080" <<  endl;
    return 0;
}

TypeError: Cannot read property 'type' of undefined

Of course, this limitaion could be easily worked around with a kostyl like punycode, but it seems to be very unpleasant.

Text below is in Russian

Доброго времени суток, товарищи!

Текст выше - на английском. Спасибо за проделанную работу, либа годная, частично может заменить проклятую проприетарную MS Visual Studio для совсем уж первокурсников (хотя бы на время, пока они её крякают). И вообще, да здравствует импортозамещение!

Однако, незадача: кириллический текст запихнуть в cout не получается. Да вообще ничего, кроме ASCII, не получается. Примеры выше. Нет, конечно, мы, суровые русские программисты, ко всяким санкциям привычны, костылей-то напаяем (хоть punycode), но всё равно неприятно.

Please add a minified/single file once every update

I would like to ask if there could be published a single file/minified version of this lib with every update, because i'm trying to use it on the web, and installing/finding a way to unify the whole lib into a single file, using node, always causes problems to me, for some reason.

That if the online demo doesn't already use the last version avaliable(Could someone inform me about that?). In that case, I might just use it.

stdlib.cpp test fails

grunt test output includes this:

  1. cincout stdlib stdlib.cpp an error occurred:
  AssertionError: false == true
  + expected - actual

  -false
  +true

  at Context.<anonymous> (test/test.js:109:14)
  1. cincout stdlib stdlib.cpp should match expected output:
  AssertionError: expected '' to equal '1.234\n1234\n1234\n1234\n2\n1\n1234\n2\n1'
  + expected - actual

  +1.234
  +1234
  +1234
  +1234
  +2
  +1
  +1234
  +2
  +1

  at Context.<anonymous> (test/test.js:117:25)

parse error when run "hello world" program

#include <iostream>

int main() {
    std::cout << "hello world" << std::endl;
    return 0;
}

error info:

Error: ERROR: Parsing Failure:
line 4 (column 8): int main() {\n    std::cout << "hello wor
----------------------------------------^
Expected "!=", "%", "%=", "&", "&&", "&=", "(", "*", "*=", "+", "++", "+=", ",", "-", "--", "-=", "->", ".", "/", "/*", "//", "/=", ";", "<", "<<", "<<=", "<=", "=", "==", ">", ">=", ">>", ">>=", "?", "[", "\\U", "\\u", "^", "^=", "__attribute__", "_stdcall", "auto", "const", "extern", "inline", "register", "static", "|", "|=", "||", [ \n\r\t\u000B\u000C], [0-9], [A-Z], [_] or [a-z] but ":" found.

is this a valid error

Felix, not sure if this should flag an error or not.
#include
using namespace std;
int main() {
unsigned char a = 23;
a << 4;
return a;
}

returns 5:5 overflow of 368(unsigned char)

I am using this as a C simulator, where I would expect it to just do the shift and loose the bits and not create an error.
I guess I could modify CRuntime::val in rt.coffee to change this behaviour
thanks

ctime

As you've asked, I am submitting the issue that I'll try to implement the time function from ctime.
I'm going to try this because I don't know how to use srand without time.

demo page - load/save state from/to url fragment ID / hash string

please include this feature on the demo page, so we can link to demo code

sample implementation:

Main = React.createClass({
  // ....
  saveToUrl: function(e) {
    if (e != null) {
      e.preventDefault();
    }
    return setFragmentFromState(this.state);
  },
  loadFromUrl: function(e) {
    if (e != null) {
      e.preventDefault();
    }
    return this.setState(
      getStateDiffFromFragment()
    );
  },
var parseQueryString = function(input) {
  // similar to PHP $_GET[key]
  // return array of values if key is repeated in query string
  let queryObject = {};
  const re = /([^?&=]+)(?:=([^&]+))?/g;
  let match;
  while (match = re.exec(input)) {
    const [k, v] = [
      decodeURIComponent(match[1]),
      // empty value = true
      (decodeURIComponent(match[2]) || true)
    ];
    if (k in queryObject) {
      if (Array.isArray(queryObject[k])) {
        queryObject[k].push(v);
      } else {
        queryObject[k] = [queryObject[k], v];
    }} else {
      queryObject[k] = v;
    }
  }
  return queryObject;
};

// keys shared between state and fragment query string
// key "code" is always shared
const fragmentKeys = ["input"];

var getStateDiffFromFragment = function() {
  // fragment string format: key=val&key2=val2/codeString
  // val, val2, ... must be encoded with encodeURIComponent
  // codeString can be encoded with encodeURI
  // which is more compact than encodeURIComponent
  const idxCode = document.location.hash.indexOf("/");
  const query = parseQueryString(
    document.location.hash.substring(1, idxCode));
  let stateDiff = Object.keys(query).reduce((acc, key) => {
    if (fragmentKeys.includes(key)) {
      acc[key] = query[key];
    }
    return acc;
  }, {});
  const code = decodeURI(document.location.hash.substring(idxCode + 1));
  if (code) {
    stateDiff.code = code;
  }
  return stateDiff;
};

var setFragmentFromState = function(state) {
  document.location.hash = fragmentKeys.reduce((acc, key) => {
    if (state[key]) {
      acc.push(`${key}=${encodeURIComponent(state[key])}`)
    }
    return acc;
  }, []).join("&") + "/" + encodeURI(state.code);
};

javascript says document.location.hash, the official name is fragment identifier

limitation: old browsers limit URL length

license is creative-commons-zero, so feel free to steal my code

ps: can you make /gh-pages public? (including jsx files)

empty while loop cause debugger to

in the following code if the while loop is empty the debugger goes into a loop and cannot be stopped,
I know its not really a big issue but students writing this in their code while learning will find it hangs their browser, if there is anything in the while loop it wont of course hang up

`#include
using namespace std;
int main() {
int a=0;
while(1){

}

}`

WebWorker compatibility (for browser multi-threading)

The JSCPP distribution has exactly one error when being loaded into a WebWorker via importScripts(). That error has to do with the way window.JSCPP is assigned in index.js. WebWorkers don't have access to the window object. I was able to fix it in the context of my project by doing the following replacement.

Before:
window.JSCPP = require('./launcher').default;
After:
var JSCPP = require('./launcher').default;

This completely fixes the issue and I'm able to run C++ via a worker. However, I am not familiar enough with NodeJS to determine whether this is a reasonable solution. Just wanted to bring this to your attention.

Thank you for this project, it has been extremely helpful so far.

Mac npm install error

Hi,

On my mac os, after checking out and npm install, it shows the errors:

`Running "browserify:dist" (browserify) task

Error: Can't walk dependency graph: Cannot find module './ast' from '/Users/binchen/git/JSCPP/lib/launcher.js'
required by /Users/binchen/git/JSCPP/lib/launcher.js
Warning: Error running grunt-browserify. Use --force to continue.

Aborted due to warnings.
npm ERR! code ELIFECYCLE
npm ERR! errno 6
npm ERR! [email protected] prepublish: grunt build
npm ERR! Exit status 6
npm ERR!
npm ERR! Failed at the [email protected] prepublish script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR! /Users/binchen/.npm/_logs/2020-10-20T00_39_14_925Z-debug.log
`

Can you take a look?
Thanks.

2020-10-20T00_39_14_925Z-debug.log

grunt buils does not minify files

To use your wonderful library in production (yes, we are going to!), we should use minified files. Of course, we can minify any file ourself, but the fact that uglify task is mentioned in Gruntfile.js but not used is quite strange.

will implement cstdio scanf function

as per CONTRIBUTE.md I'm creating this issue to let others know that I'm working on this.

The following functions from cstdio are uninplemented:

int getchar(void)
char *gets(char *str)
int putchar(int char)
int puts(const char *str)
int scanf(const char *format, ...)
int sscanf(const char *str, const char *format, ...)

I'll implement them.

Uninitialized array

It seems to me that there is an error with uninitialized arrays. Elements should not be initialized to 0 as is currently the case seems to me.
For example :
#include <stdio.h>

int main() {
float tab[2];
for(int i = 0; i < 2; i++){
printf("%f, ", tab[i]);
}
return 0;
}
gives 0.00000, 0.00000 instead of NaN, NaN.

The C99 standard says that "if an object that has an automatic storage duration is not explicitly initialized, its value is undetermined." I think it's the same in C++ (or not ?).

Function Prototypes

Felix, have you / will you implemented function prototypes?

#include <iostream>

int func(int x);

int main() {
    int a = 10;
    int b = 0;    
    b = func(a);
    return 0;
}

int func(int x){
    //some code 
    return 2 * x;
}

tried this with the latest version of gh-pages
throws an error with function prototype both int func(int x); and int func(int);
error = 4:1 is interp really an array initialization?

thanks
Bill

Request: Including pre-compiled "dist/JSCPP.js" into GitHub

Very interesting project and good work.
It would be great if an pre-compiled "dist/JSCPP.js" version could be checked in into GitHub as well.
This makes the testing and integration with other project much easier, because the file could be simple include as library instead of compiling the hole project first.

Thanks and Greetings,

  • Markus B.

C++ Typing error

As referred to from: viebel/klipse#300

Code that is invalid and should not compile is instead being executed and run up until the point in the source code that it fails.

The linked issue contains of note:

I was attempting to demonstrate C++ type errors that happen at compile time in a demonstration post using klipse, however the typing error seemed to happen at run-time instead of parsing/compilation time as prior lines of code were run and output information before the typing error was hit, which completely destroyed my argument for C++ that catching type errors at compile-time prior to run-time helped prevent classes of errors before the code is executed. ^.^;

Just go to any of the C++ fields at: http://blog.overminddl1.com/posts/programming-language-differences-compilation/

Then input:

#include <iostream>
using namespace std;
int main() {
  char *hw = "Hello World!";
  cout << hw << endl;
  cout << 2 * hw << endl;
  return 0;
}

I'd expect it to just print out:

6:11 unsigned int does not support * on char[13]

Instead it prints:

Hello World!

6:11 unsigned int does not support * on char[13]

Unable to add method with string parameter

HI,

I'm trying to create a method that accepts a string but can't get it to work.

I've tweaked dummy_class_foo.ts like this:

/* eslint-disable no-shadow */
import {ArrayVariable, CRuntime, IntVariable, ObjectVariable, Variable} from "../rt";

export = {
    load(rt: CRuntime) {
        const type = rt.newClass("Foo", [{
            name: "x",
            type: rt.intTypeLiteral,
            initialize(rt, _this) { return rt.val(rt.intTypeLiteral, 2, true); }
        }, {
            name: "y",
            type: rt.intTypeLiteral,
            initialize(rt, _this) { return rt.val(rt.intTypeLiteral, -2, true); }
        }
        ]);
        const typeSig = rt.getTypeSignature(type);
        rt.types[typeSig].father = "object";
        

        // ***********       My modifications are from here down...

        const pchar = rt.normalPointerType(rt.charTypeLiteral);

        const _plusX = function (rt: CRuntime, _this: ObjectVariable, t: ArrayVariable) {
            const newValue = 0;
            // do something with `t`
            return rt.val(rt.intTypeLiteral, newValue, false);
        };

        return rt.regFunc(_plusX, type, "plusX", [pchar], rt.intTypeLiteral);
    }
};

The test code is:

    var code = "#include <iostream>"+
        "#include <foo>"+
        "using namespace std;"+
        "int main() {"+
        "    Foo foo;"+
        "    cout << foo.x << ',' << foo.y << endl;"+
        '    cout << foo.plusX("hello") << endl;'+
        "    return 0;"+
        "}";

The error is:

[Error] Error: 1:99 no method plusX in [object Object] accepts char[6]
	dispatchException (JSCPP.es5.min.js:1:334009)
	(anonymous function) (JSCPP.es5.min.js:1:330361)
	run (JSCPP.es5.min.js:1:220827)
	Global Code (class.html:31)

Any help would be great, thanks.

short not overflowing

Hi Felix
In the code below there should be an overflow as the type used is a short, but it appears that the calculation is done as an int?
thanks
Bill

#include <iostream>
using namespace std;
#include <stdio.h>

unsigned short a = 900; //short is a 16bit type
unsigned char p =0;

int main() {
  p = (a*100)/1000;
  printf("a:%d\n",a);
  printf("p:%d\n",p);
  return 0;
}

Refactor the code into ES6 JS

I refactored code from ES5 JS to CoffeeScript a while ago because at that time ES6 is not that popular and without babel and eslint supporting ES6, CoffeeScript was much more appealing.

Now that ES6 JS is on par (actually surpassing) with CoffeeScript in terms of readability, maintanability and popularity. I think it is time to switch back to JS.

It is also a good opportunity to review and refactor the legacy code.

Update in 2020: Now Typescript is better than ES6 JS.

Error on matrix initialization

Consider the following code:

int main() {
    int matrix[2][2] = {{1, 2}, {3, 4}};
    return 0;
}

I should init the matrix with the values 1, 2, 3, and 4; however, the following error is issued: 2:5 dimensions do not agree, 2 != 2

srand()

In usual C++, srand() function is often used to really randomize rand() (or else rand() will return one random but fixed number - to help with debug).
In JSCPP, rand() returns different numbers each time. I don't know is it correct or not, but I'll be very glad if srand() function (maybe, empty?) will exist.

8 bit types

Hi Felix
I am beginning to look more deeply at your code and have begun integrating it into a javascript version of my microcontroller visualiser
I notice that the following does not work quite right,
int main()
{
unsigned char x = 126;
x++; //127
x++; //128
x++; //129
x++; //130
x=x+1; //result is unknown:unknown overflow of 128(signed char)
return 0;
}
// signed char does the same

I am very interested in 8 and 16 bit types as I would like to use this as the basis for a microcontroller simulator.
I see defaults.coffee handles the sizes of these, but in rt.coffee only 7bit char and 32bit ints appear to be used? I really do not understand your code so I am sorry i am not to be able to contribute to this very much at this stage.

thanks
Bill

解析模版语法

我想用pegjs解析模板语法,比如 vecotr v; 改了一下文法文件但是 < 始终被识别为小于符号。可以大概提供下思路吗,谢谢🙏

BUG: cstdio and cstring are conflicting.

if you include cstdio and cstring on same cpp file you get an exception saying that some methods are already defined, this is happening because during the load of cstdio cstring is being implicit loaded by this line

rt.include "cstring"

I've already started working on a fix to this, bellow follows a test case to see the problem happening:

#include <stdio.h>
#include <string.h>

int main()
{
    printf("test - ok");
    return 0;
}

Goto

I am looking into using your interpreter to visualise interrupt processing of an embedded system
Having a goto type function that is under external control might make it possible,
Which parts of your code should I begin to think about to implement this
thanks

Error when try to use strcat().

I visited https://felixhao28.github.io/JSCPP/ and try to input the code as:

#include <iostream>
#include <string.h>

using namespace std;

int main () {

   char str1[10] = "111";
   char str2[10] = "222";
   char str3[10];

   // concatenates str1 and str2
   strcat( str1, str2);
   cout << "strcat( str1, str2): " << str1 << endl;

   return 0;
}

However, it's error as:
"TypeError: Cannot read property 'type' of undefined"

Fix <math.h>pow function

Here is my correction

rt.regFunc ((rt, _this, x, y) ->
        rt.val(tDouble,x.v ** y.v)
    ), g, "pow", [ tDouble,tDouble ], tDouble

CPP -> JS compiler

Is it possible not to execute C++ code but only translate it to JS, which could be written to a file and then executed?

Scope

I tried the following and it works, but m should not be in scope

#include
using namespace std;

int g=1;

int main() {
int m = 2;
cout << "scope test" << endl;
func1();
return 0;
}

void func1(){
int f1 = 10;
cout << g << endl;
cout << f1 << endl;
cout << m << endl; //should not be in scope but is?
}

global vars not showing in debugger.variable

Hi Felix
I noticed today that globals were not being shown by debugger.variable
e.g.

//vars in scopes
int g=3;
int y=10;
int main()
{
    int x =  2;
    x = func(x);
    x += y;
    return x;
}
int func(int a)
{
    int b =a;
    b = a * a;
    return b;
}

Debugging in firefox I realised that in debugger.prototype.variable
the line

for scopeIndex in [@rt.scope.length - 1...0] by -1

has three dots so I think it doesn't go down to the global scope '0'

I altered debugger.js

for (scopeIndex = i = ref = this.rt.scope.length - 1; i > 0; scopeIndex = i += -1) {

to

for (scopeIndex = i = ref = this.rt.scope.length - 1; i >= 0; scopeIndex = i += -1) {

changing this meant I also had to change the line in the JS

if (typeof val === "object" && "t" in val && "v" in val)

to

if (typeof val === "object" && "t" in val && 'name' in val.t && "v" in val)

you do of course have a fuller perspective on my alteration to the code and know its wider effects
cheers
Bill

Issue with function(a,b,c)

Any less than trivial program stops (for me) at

    function(a, b, c) {
        return a.push(b).concat(c);
    },

where a is an Array, which, at least if you didn't do any magic to Array, would have a.push(b) return a number (the new length of the array) and then try to run .concat(c) on that number, which does not work. Am I doing something wrong?

Way to reproduce: make a new C source with only;

extern const byte title[];

and execute. I use the latest master jscpp_page.js file from github.

demo page - make code editor resizable

this should do it ....

<AceEditor
  onLoad={editorInstance => {
    editorInstance.container.style.resize = "both";
    // mouseup = css resize end
    document.addEventListener("mouseup", e => (
      editorInstance.resize()
    ));
  }}
/>

here is a demo fiddle

C++ to WebAssembly in a browser

I would like to make small chunks of my JS code faster (short functions, which perform heavy computation). I was thinking about writing them in WebAssembly manually, but writing them in C would be much more comfortable.

Would you like to extend your tool, so it can compile the C code into WebAssembly? I don't want to make a mess in my computer by installing heavy native compilers, just to translate several lines of C code into WebAssembly.

Javascript-defined classes

Thanks for this great little interpreter!

I'd like to allow instantiation of a class defined in javascript. Even though it isn't supported, I'm curious to know what would roughly be needed to achieve this. Easy or hard? Any steps I'd need to take?

A simple include defining the Foo class, would be (hope you're ok with ES6):

// foo.js
export function load(rt) {
  const type = rt.newClass('Foo', []);

  const Foo = {
    t: type,
    v: {
      members: {},
    },
  };
  rt.scope[0]['Foo'] = Foo;

  rt.types[rt.getTypeSignature(Foo.t)] = {
    '#father': 'object',
  };
};

When instantiating an object with Foo myFoo, I get the error No default value for object.
Changing this to Foo myFoo() tells me value is undefined in CRuntime::cast.

Would you be able to give any pointers?

Better Display Values

I was wondering if we could provide better display values, after having a look on the code I think there could be done with small adjustments.

  • Is there any straightforward solution to provide a better display for multidimensional arrays? So far, starting from 2D the display value is: [...] for each element. There is a parameter which stops the recursion after first dimension, are any other sections which are intersecting?
  • Could the values from pointers include (if not an actual address) the name of the variable being pointed?
  • Functions are being represented as $[object], we could use a suggestive placeholder.

bit invert ~ issue

Hi Felix
been busy with other work, back to testing some code and found something not quite right with bit inversion,
noticed it first with this

unsigned char A=0;
unsigned char B=0x80;
int main() {
    A = ~B;
    return 0;
}

returns: TypeError: type is undefined (also the error doesn't show a line number)

also this

unsigned int A=0;
unsigned int B=0x0080;  //also B=0x80 or B=128;
int main() {
    A = ~B;
    return 0;
} 

returns: 4:5 cannot cast negative value to unsigned int

in defaults.coffee line 308 ...
ret = ~l.v
appears to invert everything using signed 64bit(?) int and therefore always thinks its a negative number

Also will you support binary literals 0b01010111 ?

thanks
Bill

Unable to add extra methods

Hi,

I'm trying to create a custom class in ts but I'm running into a couple of issues, this is one...

If I add a second method, by simply modifying dummy_class_foo.ts like this:

        rt.regFunc(_plusX, type, "plusX", [rt.intTypeLiteral], rt.intTypeLiteral);
        rt.regFunc(_plusX, type, "plusX2", [rt.intTypeLiteral], rt.intTypeLiteral);

Using the class_basics in an HTML file:

    var code = "#include <iostream>"+
        "#include <foo>"+
        "using namespace std;"+
        "int main() {"+
        "    Foo foo;"+
        "    cout << foo.x << ',' << foo.y << endl;"+
        "    cout << foo.plusX(5) << endl;"+
        "    cout << foo.plusX2(5) << endl;"+
        "    return 0;"+
        "}";

I get this error:

[Error] TypeError: undefined is not an object (evaluating 't.v')
	dispatchException (JSCPP.es5.min.js:1:334009)
	(anonymous function) (JSCPP.es5.min.js:1:330361)
	run (JSCPP.es5.min.js:1:220827)
	Global Code (class.html:32)

Any help would be greatly appreciated. Thanks.

Pointers to arrays

This code works fine , however the value of the ptr p_arr returned from the debugger, shows the array and not the value pointed to as per the attached image I would have expected the value of p_arr to display as ->11
Looking at the code it seems to me that there is only a pointer type and not an array type, so arrays are subset of pointers?

image

ambiguous method invoking, 2 compatible methods

In the following code a function prototype is used and an error is thrown

`#include
using namespace std;

int func(int x);

int main() {
int a;
cin >> a;
a = func(5); //fails
//a = func(a); //works
cout << a << endl;
return 0;
}

int func(int x){
x*= 10 ;
return x;
}`

it appears that the number '5' is of type 'unsigned int' so when getCompatibleFunction sees two possible functions (the prototype and the actual function) it throws the error.

Is this safe?

I am wondering if this would be safe to run user-provided code. Excluding denial-of-service attacks e.g. while(1){} it doesn't look like network requests could be made so I'm wondering if this would serve as a safe "sandbox" for running C++ based code.

typedef

Hi Felix
does typedef work correctly?
I get an error with

typedef unsigned int WORD;

thanks
Bill

Variable scope

Hi you have a wonderful piece of code here. Just fantastic,Where do you teach?
Anyway,
I noticed that the scope of debugger.variable() only returns variables within the current scope e.g. in this code

#include <iostream>
int main() {
    int addtome;
    cin >> addtome;
    for (int index=0; index<3; index++)
     {
        addtome+=index;
     }
     cout<< addme << endl;
    return 0;
}

addtome does not show until it has exited the for loop.
I will have a look at 'Debugger.prototype.variable' myself to see if I can change it.
I would like to be able to return a list of the scopes and the vars in each scope as well.

3 years ago I wrote C and BASIC interpreters in C# as part of a visualizer for ATMEL microcontrollers and am now porting it to the web using javascript. I am a newbie with Javascript and was building a peg for C using pegjs and ACE and was looking for information on Generators and Yield and found your code. It's so much better than the recursive parser I wrote in C#! I think I will stop my own port! Now I just need to get my head around nodejs, exports, require, react, coffee, generators....

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.