procedure.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. //var assert = require("assert").ok;
  2. var Cast = require("cast.js");
  3. var Class = require("rtl.js").Class;
  4. var Code = require("code.js");
  5. var Errors = require("errors.js");
  6. var Type = require("type.js");
  7. var Arg = Class.extend({
  8. init: function(type, isVar){
  9. this.type = type;
  10. this.isVar = isVar;
  11. },
  12. description: function(){
  13. return (this.isVar ? "VAR " : "") + this.type.description();
  14. }
  15. });
  16. exports.Arg = Arg;
  17. var ProcCallGenerator = Class.extend({
  18. init: function ProcCallGenerator(codeGenerator, id, type){
  19. this.__codeGenerator = codeGenerator;
  20. this.__id = id;
  21. this.__type = type;
  22. this.__argumentsCount = 0;
  23. this.writeCode(this.prolog());
  24. },
  25. id: function(){return this.__id;},
  26. handleArgument: function(type, designator, code){
  27. var pos = this.__argumentsCount++;
  28. var isVarArg = false;
  29. if (this.__type){
  30. var expectedArguments = this.__type.arguments();
  31. if (pos >= expectedArguments.length )
  32. // ignore, handle error after parsing all arguments
  33. return;
  34. var arg = this.checkArgument(pos, type, designator);
  35. isVarArg = arg.isVar;
  36. }
  37. if (designator){
  38. var info = designator.info();
  39. if (info instanceof Type.Variable)
  40. if (info.isVar() && !isVarArg)
  41. code += ".get()";
  42. else if (!info.isVar() && isVarArg)
  43. code = designator.refCode();
  44. }
  45. var prefix = pos ? ", " : "";
  46. this.writeCode(prefix + code);
  47. },
  48. end: function(){
  49. if (this.__type){
  50. var procArgs = this.__type.arguments();
  51. if (this.__argumentsCount != procArgs.length)
  52. throw new Errors.Error(procArgs.length + " argument(s) expected, got "
  53. + this.__argumentsCount);
  54. }
  55. this.writeCode(this.epilog());
  56. return this.__codeGenerator.result();
  57. },
  58. prolog: function(){return this.__id + "(";},
  59. checkArgument: function(pos, type, designator){
  60. var arg = this.__type.arguments()[pos];
  61. var expectType = arg.type; // can be undefined for predefined functions (like NEW), dont check it in this case
  62. if (expectType && !Cast.implicit(type, expectType))
  63. throw new Errors.Error("expect '" + expectType.name() + "' type for argument "
  64. + pos + ", got '" + type.name() + "'");
  65. if (arg.isVar){
  66. if (!designator)
  67. throw new Errors.Error("expression cannot be used as VAR parameter");
  68. var info = designator.info();
  69. if (info instanceof Type.Const)
  70. throw new Errors.Error("constant cannot be used as VAR parameter");
  71. if (info.isReadOnly())
  72. throw new Errors.Error("read-only variable cannot be used as VAR parameter");
  73. }
  74. return arg;
  75. },
  76. epilog: function(){return ")";},
  77. writeCode: function(s){this.__codeGenerator.write(s);}
  78. });
  79. var ProcType = Type.Basic.extend({
  80. init: function ProcType(name, args, result, callGeneratorFactory){
  81. Type.Basic.prototype.init.bind(this)(name, "null");
  82. this.__arguments = args;
  83. this.__result = result;
  84. this.__callGeneratorFactory = callGeneratorFactory
  85. ? callGeneratorFactory
  86. : function(codeGenerator, id, type){
  87. return new ProcCallGenerator(codeGenerator, id, type);
  88. };
  89. },
  90. isProcedure: function(){return true;},
  91. define: function(args, result){
  92. this.__arguments = args;
  93. this.__result = result;
  94. },
  95. arguments: function(){return this.__arguments;},
  96. result: function(){return this.__result;},
  97. description: function(){
  98. var name = this.name();
  99. if (name)
  100. return name;
  101. return 'PROCEDURE' + this.__dumpProcArgs()
  102. + (this.__result ? ": " + this.__result.name() : "");
  103. },
  104. callGenerator: function(codeGenerator, id){
  105. return this.__callGeneratorFactory(codeGenerator, id, this);
  106. },
  107. __dumpProcArgs: function(){
  108. if (!this.__arguments.length)
  109. return this.__result ? "()" : "";
  110. var result = "(";
  111. for(var i = 0; i < this.__arguments.length; ++i){
  112. if (i)
  113. result += ", ";
  114. result += this.__arguments[i].description();
  115. }
  116. result += ")";
  117. return result;
  118. }
  119. });
  120. var TwoArgToOperatorProcCallGenerator = ProcCallGenerator.extend({
  121. init: function TwoArgToOperatorProcCallGenerator(codeGenerator, id, type, operator){
  122. ProcCallGenerator.prototype.init.bind(this)(Code.nullGenerator, id, type);
  123. this.__code = codeGenerator;
  124. this.__operator = operator;
  125. this.__firstArgumentCode = undefined;
  126. this.__secondArgumentCode = undefined;
  127. },
  128. prolog: function(id){return "";},
  129. handleArgument: function(type, designator, code){
  130. if (!this.__firstArgumentCode)
  131. this.__firstArgumentCode = code;
  132. else
  133. this.__secondArgumentCode = code;
  134. ProcCallGenerator.prototype.handleArgument.bind(this)(type, designator, code);
  135. },
  136. epilog: function(type){return "";},
  137. end: function(){
  138. return this.__operator(this.__firstArgumentCode, this.__secondArgumentCode);
  139. }
  140. });
  141. exports.predefined = [
  142. function(){
  143. var NewProcCallGenerator = ProcCallGenerator.extend({
  144. init: function NewProcCallGenerator(codeGenerator, id, type){
  145. ProcCallGenerator.prototype.init.bind(this)(codeGenerator, id, type);
  146. this.__baseType = undefined;
  147. },
  148. prolog: function(id){return "";},
  149. checkArgument: function(pos, type, designator){
  150. ProcCallGenerator.prototype.checkArgument.bind(this)(pos, type, designator);
  151. if (!(type instanceof Type.Pointer))
  152. throw new Errors.Error("POINTER variable expected, got '"
  153. + type.name() + "'");
  154. this.__baseType = type.baseType();
  155. return new Arg(type, false);
  156. },
  157. epilog: function(){return " = new " + this.__baseType.name() + "()";}
  158. });
  159. var name = "NEW";
  160. var args = [new Arg(undefined, true)];
  161. var type = new Type.Procedure(new ProcType(
  162. "predefined procedure NEW",
  163. args,
  164. undefined,
  165. function(codeGenerator, id, type){
  166. return new NewProcCallGenerator(codeGenerator, id, type);
  167. }));
  168. var symbol = new Type.Symbol(name, type);
  169. return symbol;
  170. }(),
  171. function(){
  172. var args = [new Arg(Type.basic.set, true),
  173. new Arg(Type.basic.int, false)];
  174. function operator(x, y){return x + " |= 1 << " + y;}
  175. var proc = new ProcType(
  176. "predefined procedure INCL",
  177. args,
  178. undefined,
  179. function(codeGenerator, id, type){
  180. return new TwoArgToOperatorProcCallGenerator(
  181. codeGenerator, id, type, operator);
  182. });
  183. var type = new Type.Procedure(proc);
  184. var symbol = new Type.Symbol("INCL", type);
  185. return symbol;
  186. }(),
  187. function(){
  188. var args = [new Arg(Type.basic.set, true),
  189. new Arg(Type.basic.int, false)];
  190. function operator(x, y){return x + " &= ~(1 << " + y + ")";}
  191. var proc = new ProcType(
  192. "predefined procedure EXCL",
  193. args,
  194. undefined,
  195. function(codeGenerator, id, type){
  196. return new TwoArgToOperatorProcCallGenerator(
  197. codeGenerator, id, type, operator);
  198. });
  199. var type = new Type.Procedure(proc);
  200. var symbol = new Type.Symbol("EXCL", type);
  201. return symbol;
  202. }()
  203. ];
  204. exports.CallGenerator = ProcCallGenerator;
  205. exports.Type = ProcType;