diff --git a/.eslintrc.js b/.eslintrc.js index 2a3e949..969a421 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -11,7 +11,7 @@ module.exports = { }, "parserOptions": { "ecmaVersion": 2018, - "sourceType": "commonjs" + "sourceType": "module" }, "rules": { "no-console": 0 diff --git a/cli.js b/cli.js index 69f8061..d2931f7 100755 --- a/cli.js +++ b/cli.js @@ -79,7 +79,7 @@ const processOptions = args => .map(arg => { const match = optionRegex.exec(arg) if (match) { - const [_, option, value] = match + const [, option, value] = match if (!value) { options[option] = !options[option] } else { @@ -105,4 +105,4 @@ const main = argv => { } } -return main(process.argv) +main(process.argv) diff --git a/docs/browser.e9f8be96.js b/docs/browser.e9f8be96.js index c9dea57..ced3c09 100644 --- a/docs/browser.e9f8be96.js +++ b/docs/browser.e9f8be96.js @@ -1,15 +1,15 @@ parcelRequire=function(e,r,t,n){var i,o="function"==typeof parcelRequire&&parcelRequire,u="function"==typeof require&&require;function f(t,n){if(!r[t]){if(!e[t]){var i="function"==typeof parcelRequire&&parcelRequire;if(!n&&i)return i(t,!0);if(o)return o(t,!0);if(u&&"string"==typeof t)return u(t);var c=new Error("Cannot find module '"+t+"'");throw c.code="MODULE_NOT_FOUND",c}p.resolve=function(r){return e[t][1][r]||r},p.cache={};var l=r[t]=new f.Module(t);e[t][0].call(l.exports,p,l,l.exports,this)}return r[t].exports;function p(e){return f(p.resolve(e))}}f.isParcelRequire=!0,f.Module=function(e){this.id=e,this.bundle=f,this.exports={}},f.modules=e,f.cache=r,f.parent=o,f.register=function(r,t){e[r]=[function(e,r){r.exports=t},{}]};for(var c=0;c":function(t){t.addToken(t.nextMatch("=")?h.GREATER_EQUAL:h.GREATER)},"<":function(t){t.addToken(t.nextMatch("=")?h.LESS_EQUAL:h.LESS)}," ":r,"\t":r,"\r":r,"\n":function(t){t.newline()},'"':function(t){t.handleStringLiterals()}},a=function(t){return/\d/.test(t)},l=function(t){return/[a-zA-Z_]/.test(t)},f=function(t){return l(t)||a(t)},d=function(){function n(e){t(this,n),this.source=e,this.length=e.length,this.tokens=[],this.startPosition=null,this.column=0,this.start=0,this.line=1,this.current=0}return e(n,null,[{key:"tokens",get:function(){return o}},{key:"tokenEnum",get:function(){return h}}]),e(n,[{key:"handleStringLiterals",value:function(){for(;'"'!==this.peek()&&""!==this.peek();)"\n"===this.peek()&&this.newline(),this.chomp();if(""===this.peek())throw new s("Unfinished string",this.startPosition,this.endPosition);this.chomp();var t=this.source.substring(this.start+1,this.current-1);this.addToken(h.STRING,t)}},{key:"handleNumberLiterals",value:function(){for(var t=!1;a(this.peek())||!t&&"."===this.peek();)"."===this.peek()&&(t=!0),this.chomp();var n=this.source.substring(this.start,this.current);this.addToken(h.NUMBER,parseFloat(n))}},{key:"handleIdentifiers",value:function(){for(;f(this.peek());)this.chomp();var t=this.source.substring(this.start,this.current);u[t]?this.addToken(u[t],t):this.addToken(h.IDENTIFIER,t)}},{key:"scanTokens",value:function(){for(;this.current1&&void 0!==arguments[1]?arguments[1]:null,e=this.source.substring(this.start,this.current);this.tokens.push(new k(t,e,n,new E(this.column,this.line,this.current),this.startPosition))}},{key:"increment",value:function(){this.current++,this.column++}},{key:"newline",value:function(){this.line++,this.column=0}},{key:"chomp",value:function(){return this.increment(),this.source.charAt(this.current-1)}},{key:"peek",value:function(){return this.source.charAt(this.current)}},{key:"nextMatch",value:function(t){return this.peek()===t&&(this.increment(),!0)}},{key:"endPosition",get:function(){return new E(this.column-1,this.line,this.current)}}]),n}(),E=function n(e,i,s){t(this,n),this.col=e,this.line=i,this.index=s},k=function n(e,i,s,r,o){t(this,n),this.type=e,this.lexeme=i,this.literal=s,this.startCoordinates=o,this.endCoordinates=r};module.exports=d; },{"./errors":"p8GN"}],"39eF":[function(require,module,exports) { -function t(n){return(t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(n)}function n(n,e){return!e||"object"!==t(e)&&"function"!=typeof e?i(n):e}function i(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}function e(t){return(e=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)})(t)}function o(t,n){if("function"!=typeof n&&null!==n)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(n&&n.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),n&&r(t,n)}function r(t,n){return(r=Object.setPrototypeOf||function(t,n){return t.__proto__=n,t})(t,n)}function s(t,n){if(!(t instanceof n))throw new TypeError("Cannot call a class as a function")}var h=function t(n,i,e){s(this,t),this.left=n,this.operator=i,this.right=e},u=function(t){function i(){return s(this,i),n(this,e(i).apply(this,arguments))}return o(i,h),i}(),c=function t(n,i){s(this,t),this.operator=n,this.right=i},a=function t(n){var i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"regular";s(this,t),this.context=i,this.value=n},f=function t(n){s(this,t),this.name=n},l=function t(n,i){s(this,t),this.object=n,this.name=i},p=function t(n,i,e){s(this,t),this.object=n,this.name=i,this.value=e},y=function t(n){s(this,t),this.expression=n},m=function(t){function i(t){var o,r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"regular";return s(this,i),(o=n(this,e(i).call(this,t))).context=r,o}return o(i,y),i}(),b=function(t){function i(){return s(this,i),n(this,e(i).apply(this,arguments))}return o(i,y),i}(),g=function t(n,i){s(this,t),this.name=n,this.initializer=i},d=function t(n,i){s(this,t),this.name=n,this.value=i},v=function t(n){var i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"regular";s(this,t),this.context=i,this.statements=n},S=function t(n,i,e){s(this,t),this.condition=n,this.thenBranch=i,this.elseBranch=e},w=function t(n,i){var e=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"while";s(this,t),this.context=e,this.condition=n,this.body=i},x=function t(n,i,e){s(this,t),this.callee=n,this.paren=i,this.arguments=e},O=function t(n,i){s(this,t),this.keyword=n,this.value=i},j=function t(n,i){s(this,t),this.name=n,this.methods=i},_=function t(n,i,e){s(this,t),this.name=n,this.params=i,this.bodyStatements=e};module.exports={Var:f,Binary:h,Unary:c,Block:v,Call:x,While:w,Class:j,Get:l,Set:p,Literal:a,Return:O,Logical:u,LoxFunction:_,Grouping:y,Condition:S,ExpressionStatement:m,PrintStatement:b,VarStatement:g,Assignment:d}; +function t(n){return(t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(n)}function n(n,e){return!e||"object"!==t(e)&&"function"!=typeof e?i(n):e}function i(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}function e(t){return(e=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)})(t)}function o(t,n){if("function"!=typeof n&&null!==n)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(n&&n.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),n&&r(t,n)}function r(t,n){return(r=Object.setPrototypeOf||function(t,n){return t.__proto__=n,t})(t,n)}function s(t,n){if(!(t instanceof n))throw new TypeError("Cannot call a class as a function")}var h=function t(n,i,e){s(this,t),this.left=n,this.operator=i,this.right=e},u=function(t){function i(){return s(this,i),n(this,e(i).apply(this,arguments))}return o(i,h),i}(),c=function t(n,i){s(this,t),this.operator=n,this.right=i},a=function t(n){var i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"regular";s(this,t),this.context=i,this.value=n},f=function t(n){s(this,t),this.name=n},l=function t(n,i){s(this,t),this.object=n,this.name=i},p=function t(n,i,e){s(this,t),this.object=n,this.name=i,this.value=e},y=function t(n){s(this,t),this.expression=n},m=function(t){function i(t){var o,r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"regular";return s(this,i),(o=n(this,e(i).call(this,t))).context=r,o}return o(i,y),i}(),b=function(t){function i(){return s(this,i),n(this,e(i).apply(this,arguments))}return o(i,y),i}(),d=function t(n,i){s(this,t),this.name=n,this.initializer=i},g=function t(n,i){s(this,t),this.name=n,this.value=i},v=function t(n){var i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"regular";s(this,t),this.context=i,this.statements=n},S=function t(n,i,e){s(this,t),this.condition=n,this.thenBranch=i,this.elseBranch=e},w=function t(n,i){var e=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"while";s(this,t),this.context=e,this.condition=n,this.body=i},x=function t(n,i,e){s(this,t),this.callee=n,this.paren=i,this.arguments=e},O=function t(n,i){s(this,t),this.keyword=n,this.value=i},j=function t(n){s(this,t),this.keyword=n},_=function t(n,i){s(this,t),this.name=n,this.methods=i},P=function t(n,i,e){s(this,t),this.name=n,this.params=i,this.bodyStatements=e};module.exports={Var:f,Binary:h,Unary:c,Block:v,Call:x,While:w,Class:_,Get:l,Set:p,Literal:a,Return:O,Logical:u,This:j,LoxFunction:P,Grouping:y,Condition:S,ExpressionStatement:m,PrintStatement:b,VarStatement:d,Assignment:g}; },{}],"CUYV":[function(require,module,exports) { -function e(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function t(e,t){for(var n=0;n2?i-2:0),s=2;s2?i-2:0),s=2;s1?e-1:0),i=1;i")}}]),t}(),M=function(t){function n(t){var r;return o(this,n),(r=e(this,i(n).call(this))).name=t,r}return r(n,V),u(n,[{key:"call",value:function(){return new Q(this)}},{key:"toString",value:function(){return"<".concat(this.name,">")}}]),n}(),Q=function(){function t(e){o(this,t),this.klass=e,this.fields=new Map}return u(t,[{key:"get",value:function(t){var e=t.lexeme;if(this.fields.has(e))return this.fields.get(e);throw c("Undefined property ".concat(e),t)}},{key:"set",value:function(t,e){var n=t.lexeme;this.fields.set(n,e)}},{key:"toString",value:function(){return"<+".concat(this.klass.name,">")}}]),t}(),q=function(){function t(e){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:console.log;o(this,t),this.printfunction=n,this.environment=e||new G,this.environment.setBuiltin("PI",Math.PI),this.environment.setBuiltin("cos",function(t,e){return Math.cos(e[0])}),this.environment.setBuiltin("mod",function(t,e){return e[0]%e[1]}),this.environment.setBuiltin("strlen",function(t,e){return e[0].length}),this.environment.setBuiltin("charAt",function(t,e){return e[0][e[1]]}),this.environment.setBuiltin("clock",function(){return(new Date).getTime()})}return u(t,[{key:"interpret",value:function(t){return this.evaluate(t)}},{key:"evaluate",value:function(t){return t instanceof A?this.visitBlock(t):t instanceof L?this.visitFunction(t):t instanceof x?this.visitAssignment(t):t instanceof g?this.visitClass(t):t instanceof S?this.visitGet(t):t instanceof d?this.visitSet(t):t instanceof k?this.visitLogical(t):t instanceof m?this.visitCall(t):t instanceof E?this.visitWhile(t):t instanceof C?this.visitCondition(t):t instanceof U?this.visitVarStatement(t):t instanceof _?this.visitPrintStatement(t):t instanceof B?this.visitReturnStatement(t):t instanceof O?this.visitGrouping(t):t instanceof w?this.visitGrouping(t):t instanceof b?this.visitVar(t):t instanceof p?this.visitLiteral(t):t instanceof y?this.visitUnary(t):t instanceof h?this.visitBinary(t):void 0}},{key:"visitLiteral",value:function(t){return t.value}},{key:"visitGrouping",value:function(t){return this.evaluate(t.expression)}},{key:"visitPrintStatement",value:function(t){var e=this.evaluate(t.expression);return this.printfunction(null===e?"nil":e.toString()),e}},{key:"visitFunction",value:function(t){var e=new V(t,this.environment);this.environment.set(t.name,e)}},{key:"visitLogical",value:function(t){var e=this.evaluate(t.left);if(t.operator.type===R.OR){if(j(e))return e}else if(!j(e))return e;return this.evaluate(t.right)}},{key:"visitWhile",value:function(t){for(;j(this.evaluate(t.condition));)this.evaluate(t.body);return null}},{key:"visitReturnStatement",value:function(t){var e=null;throw t.value&&(e=this.evaluate(t.value)),new v(e)}},{key:"visitVar",value:function(t){return this.environment.get(t)}},{key:"visitVarStatement",value:function(t){var e=null;return null!==t.initializer&&(e=this.evaluate(t.initializer)),this.environment.set(t.name,e),null}},{key:"visitClass",value:function(t){this.environment.set(t.name,null);var e=new M(t.name.lexeme);return this.environment.assign(t.name,e),null}},{key:"visitGet",value:function(t){var e=this.evaluate(t.object);if(e instanceof Q)return e.get(t.name);throw c("Only instances have properties",t.name)}},{key:"visitSet",value:function(t){var e=this.evaluate(t.object);if(!(e instanceof Q))throw c("Only instances have fields",t.name);var n=this.evaluate(t.value);return e.set(t.name,n)}},{key:"visitBlock",value:function(t){return this.interpretBlock(t.statements,new G(this.environment)),null}},{key:"visitCondition",value:function(t){return j(this.evaluate(t.condition))?this.evaluate(t.thenBranch):t.elseBranch&&this.evaluate(t.elseBranch),null}},{key:"interpretBlock",value:function(t,e){var n=this.environment;try{this.environment=e;var i=!0,r=!1,a=void 0;try{for(var o,s=t[Symbol.iterator]();!(i=(o=s.next()).done);i=!0){var u=o.value;this.interpret(u)}}catch(l){r=!0,a=l}finally{try{i||null==s.return||s.return()}finally{if(r)throw a}}this.environment=n}catch(c){throw this.environment=n,c}}},{key:"visitAssignment",value:function(t){var e=this.evaluate(t.value);return this.environment.assign(t.name,e),e}},{key:"visitCall",value:function(t){var e=this,n=this.evaluate(t.callee),i=t.arguments.map(function(t){return e.evaluate(t)});if(!n.call)throw c("Can only call functions and classes",t.paren);return n.call(this,i)}},{key:"visitUnary",value:function(t){var e=this.evaluate(t.right);switch(t.operator.type){case R.MINUS:return T(t.operator,e),-e;case R.BANG:return!j(e)}}},{key:"visitBinary",value:function(t){var e=this.evaluate(t.left),n=this.evaluate(t.right);switch(t.operator.type){case R.MINUS:return T(t.operator,n,e),e-n;case R.PLUS:return e+n;case R.SLASH:return T(t.operator,n,e),e/n;case R.STAR:return T(t.operator,n,e),e*n;case R.GREATER:return T(t.operator,n,e),e>n;case R.GREATER_EQUAL:return T(t.operator,n,e),e>=n;case R.LESS:return T(t.operator,n,e),e1?e-1:0),i=1;i")}}]),t}(),Q=function(t){function n(t,r){var a;return o(this,n),(a=e(this,i(n).call(this))).name=t,a.methods=r,a}return r(n,V),u(n,[{key:"call",value:function(){return new q(this)}},{key:"toString",value:function(){return"<".concat(this.name,">")}}]),n}(),q=function(){function t(e){o(this,t),this.klass=e,this.fields=new Map}return u(t,[{key:"get",value:function(t){var e=t.lexeme;if(this.fields.has(e))return this.fields.get(e);if(this.klass.methods.has(e))return this.klass.methods.get(e).bind(this);throw c('Undefined property "'.concat(e,'"'),t)}},{key:"set",value:function(t,e){var n=t.lexeme;this.fields.set(n,e)}},{key:"toString",value:function(){return"<+".concat(this.klass.name,">")}}]),t}(),I=function(){function t(e){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:console.log;o(this,t),this.printfunction=n,this.environment=e||new P,this.environment.setBuiltin("PI",Math.PI),this.environment.setBuiltin("cos",function(t,e){return Math.cos(e[0])}),this.environment.setBuiltin("mod",function(t,e){return e[0]%e[1]}),this.environment.setBuiltin("strlen",function(t,e){return e[0].length}),this.environment.setBuiltin("charAt",function(t,e){return e[0][e[1]]}),this.environment.setBuiltin("clock",function(){return(new Date).getTime()})}return u(t,[{key:"interpret",value:function(t){return this.evaluate(t)}},{key:"evaluate",value:function(t){return t instanceof L?this.visitBlock(t):t instanceof x?this.visitFunction(t):t instanceof C?this.visitAssignment(t):t instanceof d?this.visitClass(t):t instanceof g?this.visitGet(t):t instanceof w?this.visitSet(t):t instanceof b?this.visitThis(t):t instanceof k?this.visitLogical(t):t instanceof m?this.visitCall(t):t instanceof A?this.visitWhile(t):t instanceof G?this.visitCondition(t):t instanceof U?this.visitVarStatement(t):t instanceof _?this.visitPrintStatement(t):t instanceof E?this.visitReturnStatement(t):t instanceof O?this.visitGrouping(t):t instanceof B?this.visitGrouping(t):t instanceof S?this.visitVar(t):t instanceof p?this.visitLiteral(t):t instanceof y?this.visitUnary(t):t instanceof f?this.visitBinary(t):void 0}},{key:"visitLiteral",value:function(t){return t.value}},{key:"visitGrouping",value:function(t){return this.evaluate(t.expression)}},{key:"visitPrintStatement",value:function(t){var e=this.evaluate(t.expression);return this.printfunction(null===e?"nil":e.toString()),e}},{key:"visitFunction",value:function(t){var e=new V(t,this.environment);this.environment.set(t.name,e)}},{key:"visitLogical",value:function(t){var e=this.evaluate(t.left);if(t.operator.type===j.OR){if(T(e))return e}else if(!T(e))return e;return this.evaluate(t.right)}},{key:"visitWhile",value:function(t){for(;T(this.evaluate(t.condition));)this.evaluate(t.body);return null}},{key:"visitReturnStatement",value:function(t){var e=null;throw t.value&&(e=this.evaluate(t.value)),new v(e)}},{key:"visitVar",value:function(t){return this.environment.get(t)}},{key:"visitVarStatement",value:function(t){var e=null;return null!==t.initializer&&(e=this.evaluate(t.initializer)),this.environment.set(t.name,e),null}},{key:"visitClass",value:function(t){this.environment.set(t.name,null);var e=new Map,n=!0,i=!1,r=void 0;try{for(var a,o=t.methods[Symbol.iterator]();!(n=(a=o.next()).done);n=!0){var s=a.value,u=new V(s,this.environment);e.set(s.name.lexeme,u)}}catch(c){i=!0,r=c}finally{try{n||null==o.return||o.return()}finally{if(i)throw r}}var l=new Q(t.name.lexeme,e);return this.environment.assign(t.name,l),null}},{key:"visitGet",value:function(t){var e=this.evaluate(t.object);if(e instanceof q)return e.get(t.name);throw c("Only instances have properties",t.name)}},{key:"visitSet",value:function(t){var e=this.evaluate(t.object);if(!(e instanceof q))throw c("Only instances have fields",t.name);var n=this.evaluate(t.value);return e.set(t.name,n)}},{key:"visitThis",value:function(t){return this.environment.get({name:t.keyword})}},{key:"visitBlock",value:function(t){return this.interpretBlock(t.statements,new P(this.environment)),null}},{key:"visitCondition",value:function(t){return T(this.evaluate(t.condition))?this.evaluate(t.thenBranch):t.elseBranch&&this.evaluate(t.elseBranch),null}},{key:"interpretBlock",value:function(t,e){var n=this.environment;try{this.environment=e;var i=!0,r=!1,a=void 0;try{for(var o,s=t[Symbol.iterator]();!(i=(o=s.next()).done);i=!0){var u=o.value;this.interpret(u)}}catch(l){r=!0,a=l}finally{try{i||null==s.return||s.return()}finally{if(r)throw a}}this.environment=n}catch(c){throw this.environment=n,c}}},{key:"visitAssignment",value:function(t){var e=this.evaluate(t.value);return this.environment.assign(t.name,e),e}},{key:"visitCall",value:function(t){var e=this,n=this.evaluate(t.callee),i=t.arguments.map(function(t){return e.evaluate(t)});if(!n.call)throw c("Can only call functions and classes",t.paren);return n.call(this,i)}},{key:"visitUnary",value:function(t){var e=this.evaluate(t.right);switch(t.operator.type){case j.MINUS:return N(t.operator,e),-e;case j.BANG:return!T(e)}}},{key:"visitBinary",value:function(t){var e=this.evaluate(t.left),n=this.evaluate(t.right);switch(t.operator.type){case j.MINUS:return N(t.operator,n,e),e-n;case j.PLUS:return e+n;case j.SLASH:return N(t.operator,n,e),e/n;case j.STAR:return N(t.operator,n,e),e*n;case j.GREATER:return N(t.operator,n,e),e>n;case j.GREATER_EQUAL:return N(t.operator,n,e),e>=n;case j.LESS:return N(t.operator,n,e),e3&&void 0!==arguments[3]&&arguments[3],l=new r(o).scanTokens();i&&console.log(l);var u=new e(l).parse();i&&console.log(u);var v,s=new n(t,a),c=!0,p=!1,f=void 0;try{for(var w,y=u[Symbol.iterator]();!(c=(w=y.next()).done);c=!0){var d=w.value;v=s.interpret(d)}}catch(m){p=!0,f=m}finally{try{c||null==y.return||y.return()}finally{if(p)throw f}}return v},a=function(n){var o=new r(n).scanTokens();return new e(o).parse()};module.exports={run:t,parse:a,Environment:o}; },{"./tokenizer":"u74b","./parser":"CUYV","./interpreter":"lmrS","./environment":"brut"}],"9/6S":[function(require,module,exports) { @@ -17,5 +17,5 @@ function n(n){return r(n)||e(n)||t()}function t(){throw new TypeError("Invalid a },{"../types":"39eF"}],"KVVd":[function(require,module,exports) { },{}],"3hpa":[function(require,module,exports) { -"use strict";var n=require("./index"),e=require("./errors"),r=require("./transpilers/lox"),t=require("fs"),a="",i=document.getElementById("code"),o=['// Interactive Iterative Fibonacci\nvar a = 0;\nvar b = 1;\nvar limit = prompt("Limit to find fib numbers", 10000);\n\nwhile (a < limit) {\n print a;\n var temp = a;\n a = b;\n b = temp + b;\n}\n','// Linked List via Closures\nfun Link(value, next) {\n fun access(method) {\n if (method == "value") return value;\n if (method == "next") return next;\n print "unknown method " + method;\n }\n\n return access;\n}\n\nfun traverse(n, fn) {\n fn(n);\n if (n("next") != nil) {\n return traverse(n("next"), fn);\n }\n return n;\n}\n\nfun printValue (n) {\n print n("value");\n}\n\nfun blank (n) {}\n\nfun printTail (n) {\n var tail = traverse(n, blank);\n print tail("value");\n}\n\nvar list = Link(1, Link(2, Link(3, nil)));\n\ntraverse(list, printValue);\nprintTail(list, printTail);','// Kitchen Sink\nfun hello (arg1, arg2) {\n fun secondHi (helloer) {\n print helloer + " says hello";\n }\n var str = arg1 + " says hello to " + arg2;\n var maxAge = 18;\n var age;\n age = maxAge + 3;\n if (age < 21) {\n alert("Too young");\n while(age < 21) {\n age = age + 1;\n }\n } else {\n print "come on in";\n }\n for (var i = 0; i < 18; i = i + 1) {\n print i;\n }\n return age or maxAge;\n}\n\n\nprint "hello source: ";\nprint printFunctionBody(hello);\n\n// hello("boss", "man");','// Class Example\nclass Lox {\n breakfast() {\n print "Toast";\n }\n}\n\nfun hello () {\n print "Would you like some breakfast?";\n}\n\nprint Lox;\n\nprint hello;\nvar loxInstance = Lox();\n\nloxInstance.category = "Breakfast";\nprint loxInstance.category;\n\nprint Lox();\n\n// class Bagel {}\n// var bagel = Bagel();\n// print bagel; // Prints "Bagel instance".\n// print bagel.lox;'],l=o.map(function(n){return{name:n.split("\n")[0].replace(/[^\w\s]/gi,"").trim(),program:n}}),u=function(n){var r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";if(!n)return document.getElementById("error").style.maxHeight="0",null;console.error(n);var t=(0,e.formatLoxError)(n,r),a=t.oneLiner,i=t.preErrorSection,o=t.errorSection,l=t.postErrorSection,u=a;o&&(u+="
",u+="".concat(i,'').concat(o,"").concat(l)),document.getElementById("error").style.maxHeight="150px",document.getElementById("errorText").innerHTML=u},c=document.getElementById("exampleProgram"),s=l.map(function(n,e){return'")}).join("\n");c.innerHTML=s,c.onchange=function(n){var e=n.target.value;i.value=l[e].program};var m=0;if(window.location.hash){var p=+window.location.hash.replace(/[^\w\s]/gi,"").trim();p1?e[1]:null)}),a="";try{(0,n.run)(e,t,g),u(null)}catch(o){u(o,e)}};var f=document.getElementById("format");f.onclick=function(){try{i.value=(0,n.parse)(i.value).map(function(n){return(0,r.printLoxAST)(n)}).join("\n"),u(null)}catch(e){u(e,i.value)}}; +"use strict";var n=require("./index"),e=require("./errors"),r=require("./transpilers/lox"),t=require("fs"),a="",i=document.getElementById("code"),o=['// Interactive Iterative Fibonacci\nvar a = 0;\nvar b = 1;\nvar limit = prompt("Limit to find fib numbers", 10000);\n\nwhile (a < limit) {\n print a;\n var temp = a;\n a = b;\n b = temp + b;\n}\n','// Linked List via Closures\nfun Link(value, next) {\n fun access(method) {\n if (method == "value") return value;\n if (method == "next") return next;\n print "unknown method " + method;\n }\n\n return access;\n}\n\nfun traverse(n, fn) {\n fn(n);\n if (n("next") != nil) {\n return traverse(n("next"), fn);\n }\n return n;\n}\n\nfun printValue (n) {\n print n("value");\n}\n\nfun blank (n) {}\n\nfun printTail (n) {\n var tail = traverse(n, blank);\n print tail("value");\n}\n\nvar list = Link(1, Link(2, Link(3, nil)));\n\ntraverse(list, printValue);\nprintTail(list, printTail);','// Kitchen Sink\nfun hello (arg1, arg2) {\n fun secondHi (helloer) {\n print helloer + " says hello";\n }\n var str = arg1 + " says hello to " + arg2;\n var maxAge = 18;\n var age;\n age = maxAge + 3;\n if (age < 21) {\n alert("Too young");\n while(age < 21) {\n age = age + 1;\n }\n } else {\n print "come on in";\n }\n for (var i = 0; i < 18; i = i + 1) {\n print i;\n }\n return age or maxAge;\n}\n\n\nprint "hello source: ";\nprint printFunctionBody(hello);\n\n// hello("boss", "man");','// Class Example\nclass Lox {\n breakfast() {\n print "Toast";\n this.hello = "toast";\n return this.hello;\n }\n}\n\nfun hello () {\n print "Would you like some breakfast?";\n}\n\nprint Lox;\n\nprint hello;\nvar loxInstance = Lox();\n\nloxInstance.category = "Breakfast";\nprint loxInstance.category;\nprint loxInstance.breakfast();\n\nprint Lox();\n\n// class Bagel {}\n// var bagel = Bagel();\n// print bagel; // Prints "Bagel instance".\n// print bagel.lox;'],l=o.map(function(n){return{name:n.split("\n")[0].replace(/[^\w\s]/gi,"").trim(),program:n}}),u=function(n){var r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";if(!n)return document.getElementById("error").style.maxHeight="0",null;console.error(n);var t=(0,e.formatLoxError)(n,r),a=t.oneLiner,i=t.preErrorSection,o=t.errorSection,l=t.postErrorSection,u=a;o&&(u+="
",u+="".concat(i,'').concat(o,"").concat(l)),document.getElementById("error").style.maxHeight="150px",document.getElementById("errorText").innerHTML=u},c=document.getElementById("exampleProgram"),s=l.map(function(n,e){return'")}).join("\n");c.innerHTML=s,c.onchange=function(n){var e=n.target.value;i.value=l[e].program};var m=0;if(window.location.hash){var p=+window.location.hash.replace(/[^\w\s]/gi,"").trim();p1?e[1]:null)}),a="";try{(0,n.run)(e,t,g),u(null)}catch(o){u(o,e)}};var f=document.getElementById("format");f.onclick=function(){try{i.value=(0,n.parse)(i.value).map(function(n){return(0,r.printLoxAST)(n)}).join("\n"),u(null)}catch(e){u(e,i.value)}}; },{"./index":"Focm","./errors":"p8GN","./transpilers/lox":"9/6S","fs":"KVVd"}]},{},["3hpa"], null) \ No newline at end of file diff --git a/errors.js b/errors.js index e018edf..45eeb7c 100644 --- a/errors.js +++ b/errors.js @@ -12,6 +12,16 @@ class LoxError { } } +class RuntimeError extends LoxError { + constructor(msg, token) { + super( + `${nullable(token.lexeme && `at "${token.lexeme}": `)}${msg}`, + token.startCoordinates, + token.endCoordinates + ) + } +} + class ReturnError { constructor(val) { this.value = val @@ -33,12 +43,7 @@ const parseError = (msg, token) => { } } -const runtimeError = (msg, token) => - new LoxError( - `${nullable(token.lexeme && `at "${token.lexeme}": `)}${msg}`, - token.startCoordinates, - token.endCoordinates - ) +const runtimeError = (msg, token) => new RuntimeError(msg, token) const formatLoxError = (e, code) => { if (e instanceof LoxError) { @@ -55,8 +60,9 @@ const formatLoxError = (e, code) => { const postErrorSection = code.substring(e.endCoordinates.index, postErrorStart) // Print Critical Code + const errorType = e instanceof RuntimeError ? 'Runtime Error' : 'Parse Error' return { - oneLiner: `Parse Error: ${e.toString()} at ${e.endCoordinates.line}:${e.endCoordinates.col + + oneLiner: `${errorType}: ${e.toString()} at ${e.endCoordinates.line}:${e.endCoordinates.col + 1}`, preErrorSection, errorSection, diff --git a/examples/classExample.lox b/examples/classExample.lox index 23ead97..74b74e6 100644 --- a/examples/classExample.lox +++ b/examples/classExample.lox @@ -2,6 +2,8 @@ class Lox { breakfast() { print "Toast"; + this.hello = "toast"; + return this.hello; } } @@ -16,6 +18,7 @@ var loxInstance = Lox(); loxInstance.category = "Breakfast"; print loxInstance.category; +print loxInstance.breakfast(); print Lox(); diff --git a/examples/linkedList.lox b/examples/linkedList.lox new file mode 100644 index 0000000..456dd12 --- /dev/null +++ b/examples/linkedList.lox @@ -0,0 +1,42 @@ +// Linked List via Classes +class Link {} + +fun newLink (value, next) { + var link = Link(); + link.value = value; + link.next = next; + link.append + return link; +} + +var ll = newLink(1, newLink(2, newLink(3, nil))); + +fun traverse(n, fn) { + fn(n); + if (n.next != nil) { + return traverse(n.next, fn); + } + return n; +} + +fun printList(list) { + fun printNode (n) { + print n.value; + } + traverse(list, printNode); +} + +fun append(list, newNode) { + fun walk (n) {} + var end = traverse(list, walk); + end.next = newNode; + return end; +} + +print "list:"; +printList(ll); + +append(ll, newLink(40, nil)); + +print "list:"; +printList(ll); \ No newline at end of file diff --git a/interpreter.js b/interpreter.js index c5579d0..5ee6acb 100644 --- a/interpreter.js +++ b/interpreter.js @@ -9,6 +9,7 @@ const { Get, Set, Var, + This, Grouping, Return, While, @@ -57,15 +58,22 @@ class LoxCallable { return null } + bind(instance) { + const env = new Environment(this.closure) + env.set({ lexeme: 'this' }, instance) + return new LoxCallable(this.declaration, env) + } + toString() { return `<${this.declaration.name.lexeme}()>` } } class LoxClass extends LoxCallable { - constructor(name) { + constructor(name, methods) { super() this.name = name + this.methods = methods } call() { @@ -85,11 +93,11 @@ class LoxInstance { get(token) { const name = token.lexeme - if (this.fields.has(name)) { - return this.fields.get(name) - } + if (this.fields.has(name)) return this.fields.get(name) - throw runtimeError(`Undefined property ${name}`, token) + if (this.klass.methods.has(name)) return this.klass.methods.get(name).bind(this) + + throw runtimeError(`Undefined property "${name}"`, token) // return null } @@ -126,6 +134,7 @@ class Interpreter { else if (expr instanceof Class) return this.visitClass(expr) else if (expr instanceof Get) return this.visitGet(expr) else if (expr instanceof Set) return this.visitSet(expr) + else if (expr instanceof This) return this.visitThis(expr) else if (expr instanceof Logical) return this.visitLogical(expr) else if (expr instanceof Call) return this.visitCall(expr) else if (expr instanceof While) return this.visitWhile(expr) @@ -200,7 +209,12 @@ class Interpreter { visitClass(stmt) { // We set the name before initializing it so classes can self-reference this.environment.set(stmt.name, null) - const klass = new LoxClass(stmt.name.lexeme) + const methods = new Map() + for (let method of stmt.methods) { + const fun = new LoxCallable(method, this.environment) + methods.set(method.name.lexeme, fun) + } + const klass = new LoxClass(stmt.name.lexeme, methods) this.environment.assign(stmt.name, klass) return null } @@ -225,6 +239,10 @@ class Interpreter { return object.set(expr.name, val) } + visitThis(expr) { + return this.environment.get({ name: expr.keyword }) + } + visitBlock(expr) { this.interpretBlock(expr.statements, new Environment(this.environment)) return null diff --git a/loxfmt.js b/loxfmt.js index 3a698a5..da0bc62 100755 --- a/loxfmt.js +++ b/loxfmt.js @@ -80,4 +80,4 @@ const main = argv => { } } -return main(process.argv) +main(process.argv) diff --git a/parser.js b/parser.js index 0fda4be..ca38f29 100644 --- a/parser.js +++ b/parser.js @@ -9,6 +9,7 @@ const { Class, Get, Set, + This, Grouping, Return, LoxFunction, @@ -262,6 +263,7 @@ class Parser { call() { let expr = this.primary() while (true) { + //eslint-disable-line if (this.match(token.LEFT_PAREN)) { expr = this.finishCall(expr) } else if (this.match(token.DOT)) { @@ -291,6 +293,7 @@ class Parser { if (this.match(token.TRUE)) return new Literal(true) if (this.match(token.NIL)) return new Literal(null) if (this.match(token.NUMBER, token.STRING)) return new Literal(this.previous().literal) + if (this.match(token.THIS)) return new This(this.previous()) if (this.match(token.IDENTIFIER)) return new Var(this.previous()) if (this.match(token.LEFT_PAREN)) { diff --git a/types.js b/types.js index dba5bad..730aaf7 100644 --- a/types.js +++ b/types.js @@ -109,6 +109,12 @@ class Return { } } +class This { + constructor(keyword) { + this.keyword = keyword + } +} + class Class { constructor(name, methods) { this.name = name @@ -137,6 +143,7 @@ module.exports = { Literal, Return, Logical, + This, LoxFunction, Grouping, Condition,