diff --git a/docs/browser.e9f8be96.js b/docs/browser.e9f8be96.js index 31f8523..57e24a6 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; +function t(t,n){if(!(t instanceof n))throw new TypeError("Cannot call a class as a function")}function n(t,n){for(var e=0;e":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}(),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}; +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){var e=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;s(this,t),this.name=n,this.methods=i,this.superclass=e},P=function t(n,i){s(this,t),this.keyword=n,this.method=i},k=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,Super:P,Literal:a,Return:O,Logical:u,This:j,LoxFunction:k,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;i2&&void 0!==arguments[2]&&arguments[2],i=new P(this.closure),r=0;r")}}]),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(t,e){var n=new q(this),i=this.methods.get("init");return i&&i.bind(n).call(t,e,!0),n}},{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 y?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 m?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),e1?e-1:0),i=1;i2&&void 0!==arguments[2]&&arguments[2],i=new R(this.closure),r=0;r")}}]),t}(),q=function(t){function n(t,r,a){var o;return s(this,n),(o=e(this,i(n).call(this))).name=t,o.methods=r,o.superclass=a,o}return r(n,Q),u(n,[{key:"call",value:function(t,e){var n=new I(this),i=this.methods.get("init");return i&&i.bind(n).call(t,e,!0),n}},{key:"getMethod",value:function(t,e){return this.methods.has(t)?this.methods.get(t).bind(e):this.superclass?this.superclass.getMethod(t,e):null}},{key:"toString",value:function(){return"<".concat(this.name,">")}}]),n}(),I=function(){function t(e){s(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);var n=this.klass.getMethod(e,this);if(n)return n;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}(),z=function(){function t(e){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:console.log;s(this,t),this.printfunction=n,this.environment=e||new R,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 O?this.visitFunction(t):t instanceof G?this.visitAssignment(t):t instanceof g?this.visitClass(t):t instanceof w?this.visitGet(t):t instanceof S?this.visitSet(t):t instanceof B?this.visitThis(t):t instanceof k?this.visitSuper(t):t instanceof d?this.visitLogical(t):t instanceof y?this.visitCall(t):t instanceof A?this.visitWhile(t):t instanceof P?this.visitCondition(t):t instanceof _?this.visitVarStatement(t):t instanceof C?this.visitPrintStatement(t):t instanceof E?this.visitReturnStatement(t):t instanceof U?this.visitGrouping(t):t instanceof x?this.visitGrouping(t):t instanceof b?this.visitVar(t):t instanceof p?this.visitLiteral(t):t instanceof m?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 Q(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){var e=null;if(t.superclass&&!((e=this.evaluate(t.superclass))instanceof q))throw c("Superclass must be a class",t.superclass.name);this.environment.set(t.name,null),e&&(this.environment=new R(this.environment),this.environment.set({lexeme:"super"},e));var n=new Map,i=!0,r=!1,a=void 0;try{for(var s,o=t.methods[Symbol.iterator]();!(i=(s=o.next()).done);i=!0){var u=s.value,l=new Q(u,this.environment);n.set(u.name.lexeme,l)}}catch(h){r=!0,a=h}finally{try{i||null==o.return||o.return()}finally{if(r)throw a}}var v=new q(t.name.lexeme,n,e);return e&&(this.environment=this.environment.enclosing),this.environment.assign(t.name,v),null}},{key:"visitGet",value:function(t){var e=this.evaluate(t.object);if(e instanceof I)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 I))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:"visitSuper",value:function(t){var e=t.method.lexeme,n=this.environment.get(new b(t.keyword)),i=this.environment.get(new b({lexeme:"this"})),r=n.getMethod(e,i);if(!r)throw c('Undefined property "'.concat(e,'"'),t.method);return r}},{key:"visitBlock",value:function(t){return this.interpretBlock(t.statements,new R(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 s,o=t[Symbol.iterator]();!(i=(s=o.next()).done);i=!0){var u=s.value;this.interpret(u)}}catch(l){r=!0,a=l}finally{try{i||null==o.return||o.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 V(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 V(t.operator,n,e),e-n;case j.PLUS:return e+n;case j.SLASH:return V(t.operator,n,e),e/n;case j.STAR:return V(t.operator,n,e),e*n;case j.GREATER:return V(t.operator,n,e),e>n;case j.GREATER_EQUAL:return V(t.operator,n,e),e>=n;case j.LESS:return V(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) { @@ -18,5 +18,5 @@ function n(n){return r(n)||e(n)||t()}function t(){throw new TypeError("Invalid a },{}],"3hpa":[function(require,module,exports) { var global = arguments[3]; -var n=arguments[3],e=require("./index"),t=require("./errors"),r=require("./transpilers/lox"),i=require("fs"),a="",l=document.getElementById("code"),o=['// Interactive Iterative Fibonacci\nvar a = 0;\nvar b = 1;\nvar limit = prompt("Limit to find fib numbers", 10000);\n// var limit = 1000000;\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 {\n print 23;\n }\n if (age < 21) {\n print "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: ";\n// print printFunctionBody(hello);\n\nhello("boss", "man");','// Linked List via Classes\nclass Link {\n init(value, next) {\n this.value = value;\n this.next = next;\n }\n}\n\nvar ll = Link(1, Link(2, Link(3, nil)));\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 printList(list) {\n fun printNode (n) {\n print n.value;\n }\n traverse(list, printNode);\n}\n\nfun append(list, newNode) {\n fun walk (n) {}\n var end = traverse(list, walk);\n end.next = newNode;\n return end;\n}\n\nprint "list:";\nprintList(ll);\n\nappend(ll, Link(40, nil));\n\nprint "list:";\nprintList(ll);','// 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;'],u=o.map(function(n){return{name:n.split("\n")[0].replace(/[^\w\s]/gi,"").trim(),program:n}}),s=function(n){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";if(!n)return document.getElementById("error").style.maxHeight="0",null;console.error(n);var r=(0,t.formatLoxError)(n,e),i=r.oneLiner,a=r.preErrorSection,l=r.errorSection,o=r.postErrorSection,u=i;l&&(u+="
",u+="".concat(a,'').concat(l,"").concat(o)),document.getElementById("error").style.maxHeight="150px",document.getElementById("errorText").innerHTML=u},c=document.getElementById("exampleProgram"),p=u.map(function(n,e){return'")}).join("\n");c.innerHTML=p,c.onchange=function(n){var e=n.target.value;l.value=u[e].program};var m=0;if(window.location.hash){var v=+window.location.hash.replace(/[^\w\s]/gi,"").trim();v1?e[1]:null)}),a="";try{(0,e.run)(n,t,f),s(null)}catch(i){s(i,n)}};var d=document.getElementById("format");d.onclick=function(){try{var t=(0,e.parse)(l.value);n.ast=t,l.value=t.map(function(n){return(0,r.printLoxAST)(n)}).join("\n"),s(null)}catch(i){s(i,l.value)}}; +var n=arguments[3],e=require("./index"),t=require("./errors"),r=require("./transpilers/lox"),i=require("fs"),a="",o=document.getElementById("code"),l=['// Interactive Iterative Fibonacci\nvar a = 0;\nvar b = 1;\nvar limit = prompt("Limit to find fib numbers", 10000);\n// var limit = 1000000;\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);','// 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 {\n print 23;\n }\n if (age < 21) {\n print "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: ";\n// print printFunctionBody(hello);\n\nhello("boss", "man");','// Linked List via Classes\nclass Link {\n init(value, next) {\n this.value = value;\n this.next = next;\n }\n}\n\nvar ll = Link(1, Link(2, Link(3, nil)));\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 printList(list) {\n fun printNode (n) {\n print n.value;\n }\n traverse(list, printNode);\n}\n\nfun append(list, newNode) {\n fun walk (n) {}\n var end = traverse(list, walk);\n end.next = newNode;\n return end;\n}\n\nprint "list:";\nprintList(ll);\n\nappend(ll, Link(40, nil));\n\nprint "list:";\nprintList(ll);','// Class Example\nclass Doughnut {\n init(name) {\n this.name = name;\n }\n\n cook() {\n var name = "";\n if (this.name) {\n name = this.name + " ";\n }\n print "You fry the " + name + "Doughnut until golden brown.";\n }\n}\n\nclass BostonCream < Doughnut {\n init() {\n super.init("Boston Cream");\n }\n}\n\nDoughnut("Cruller").cook();\n\nBostonCream().cook();\n\nfun hello () {\n print "Would you like some breakfast?";\n}\n\nprint BostonCream;\nprint Doughnut;\nprint BostonCream();\nprint hello;'],u=l.map(function(n){return{name:n.split("\n")[0].replace(/[^\w\s]/gi,"").trim(),program:n}}),s=function(n){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";if(!n)return document.getElementById("error").style.maxHeight="0",null;console.error(n);var r=(0,t.formatLoxError)(n,e),i=r.oneLiner,a=r.preErrorSection,o=r.errorSection,l=r.postErrorSection,u=i;o&&(u+="
",u+="".concat(a,'').concat(o,"").concat(l)),document.getElementById("error").style.maxHeight="150px",document.getElementById("errorText").innerHTML=u},c=document.getElementById("exampleProgram"),m=u.map(function(n,e){return'")}).join("\n");c.innerHTML=m,c.onchange=function(n){var e=n.target.value;o.value=u[e].program};var p=0;if(window.location.hash){var v=+window.location.hash.replace(/[^\w\s]/gi,"").trim();v1?e[1]:null)}),a="";try{(0,e.run)(n,t,f),s(null)}catch(i){s(i,n)}};var g=document.getElementById("format");g.onclick=function(){try{var t=(0,e.parse)(o.value);n.ast=t,o.value=t.map(function(n){return(0,r.printLoxAST)(n)}).join("\n"),s(null)}catch(i){s(i,o.value)}}; },{"./index":"Focm","./errors":"p8GN","./transpilers/lox":"9/6S","fs":"KVVd"}]},{},["3hpa"], null) \ No newline at end of file diff --git a/examples/classExample.lox b/examples/classExample.lox index 74b74e6..a3b76e2 100644 --- a/examples/classExample.lox +++ b/examples/classExample.lox @@ -1,28 +1,33 @@ // Class Example -class Lox { - breakfast() { - print "Toast"; - this.hello = "toast"; - return this.hello; +class Doughnut { + init(name) { + this.name = name; } -} -fun hello () { - print "Would you like some breakfast?"; + cook() { + var name = ""; + if (this.name) { + name = this.name + " "; + } + print "You fry the " + name + "Doughnut until golden brown."; + } } -print Lox; +class BostonCream < Doughnut { + init() { + super.init("Boston Cream"); + } +} -print hello; -var loxInstance = Lox(); +Doughnut("Cruller").cook(); -loxInstance.category = "Breakfast"; -print loxInstance.category; -print loxInstance.breakfast(); +BostonCream().cook(); -print Lox(); +fun hello () { + print "Would you like some breakfast?"; +} -// class Bagel {} -// var bagel = Bagel(); -// print bagel; // Prints "Bagel instance". -// print bagel.lox; \ No newline at end of file +print BostonCream; +print Doughnut; +print BostonCream(); +print hello; \ No newline at end of file diff --git a/interpreter.js b/interpreter.js index 1d36e0c..97b50af 100644 --- a/interpreter.js +++ b/interpreter.js @@ -6,6 +6,7 @@ const { Literal, Logical, Class, + Super, Get, Set, Var, @@ -73,10 +74,11 @@ class LoxCallable { } class LoxClass extends LoxCallable { - constructor(name, methods) { + constructor(name, methods, superclass) { super() this.name = name this.methods = methods + this.superclass = superclass } call(interpreter, args) { @@ -86,6 +88,14 @@ class LoxClass extends LoxCallable { return instance } + getMethod(name, instance) { + if (this.methods.has(name)) return this.methods.get(name).bind(instance) + + if (this.superclass) return this.superclass.getMethod(name, instance) + + return null + } + toString() { return `<${this.name}>` } @@ -101,7 +111,8 @@ class LoxInstance { const name = token.lexeme if (this.fields.has(name)) return this.fields.get(name) - if (this.klass.methods.has(name)) return this.klass.methods.get(name).bind(this) + const method = this.klass.getMethod(name, this) + if (method) return method throw runtimeError(`Undefined property "${name}"`, token) // return null @@ -141,6 +152,7 @@ class Interpreter { 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 Super) return this.visitSuper(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) @@ -213,14 +225,32 @@ class Interpreter { } visitClass(stmt) { + let superClass = null + if (stmt.superclass) { + superClass = this.evaluate(stmt.superclass) + if (!(superClass instanceof LoxClass)) + throw runtimeError('Superclass must be a class', stmt.superclass.name) + } + // We set the name before initializing it so classes can self-reference this.environment.set(stmt.name, null) + + if (superClass) { + this.environment = new Environment(this.environment) + this.environment.set({ lexeme: 'super' }, superClass) + } + 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) + + const klass = new LoxClass(stmt.name.lexeme, methods, superClass) + + // Pop "Super" off the environment after creating the class + if (superClass) this.environment = this.environment.enclosing + this.environment.assign(stmt.name, klass) return null } @@ -249,6 +279,17 @@ class Interpreter { return this.environment.get({ name: expr.keyword }) } + visitSuper(expr) { + const methodName = expr.method.lexeme + const superclass = this.environment.get(new Var(expr.keyword)) + const instance = this.environment.get(new Var({ lexeme: 'this' })) + const method = superclass.getMethod(methodName, instance) + if (!method) { + throw runtimeError(`Undefined property "${methodName}"`, expr.method) + } + return method + } + visitBlock(expr) { this.interpretBlock(expr.statements, new Environment(this.environment)) return null diff --git a/parser.js b/parser.js index a2b61be..18ee34c 100644 --- a/parser.js +++ b/parser.js @@ -7,6 +7,7 @@ const { Literal, While, Class, + Super, Get, Set, This, @@ -53,6 +54,12 @@ class Parser { classDeclaration() { const name = this.consume(token.IDENTIFIER, `Expected class name`) + + let superClass = null + if (this.match(token.LESS)) { + superClass = new Var(this.consume(token.IDENTIFIER, `Expected superclass name after "<"`)) + } + this.consume(token.LEFT_BRACE, 'expected "{" before class body') let methods = [] @@ -61,7 +68,7 @@ class Parser { } this.consume(token.RIGHT_BRACE, 'expected "}" after class body') - return new Class(name, methods) + return new Class(name, methods, superClass) } fun(type) { @@ -293,6 +300,12 @@ 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.SUPER)) { + const keyword = this.previous() + this.consume(token.DOT, 'Expected "." after super statement') + const method = this.consume(token.IDENTIFIER, 'Expected superclass method name') + return new Super(keyword, method) + } if (this.match(token.THIS)) return new This(this.previous()) if (this.match(token.IDENTIFIER)) return new Var(this.previous()) @@ -302,8 +315,6 @@ class Parser { return new Grouping(expr) } - console.log('primary') - throw ParseError('Expected Expression', this.peek()) } diff --git a/tokenizer.js b/tokenizer.js index f72907a..b998085 100644 --- a/tokenizer.js +++ b/tokenizer.js @@ -177,7 +177,6 @@ class Tokenizer { this.handleIdentifiers() } else { // Column isn't -1 because we haven't iterated column yet - console.log(LoxError) throw new LoxError( `Unexpected character ${c}`, this.startPosition, diff --git a/types.js b/types.js index 730aaf7..4a5cec4 100644 --- a/types.js +++ b/types.js @@ -116,9 +116,17 @@ class This { } class Class { - constructor(name, methods) { + constructor(name, methods, superclass = null) { this.name = name this.methods = methods + this.superclass = superclass + } +} + +class Super { + constructor(keyword, method) { + this.keyword = keyword + this.method = method } } @@ -140,6 +148,7 @@ module.exports = { Class, Get, Set, + Super, Literal, Return, Logical,