Compiler-Core.st 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. Smalltalk createPackage: 'Compiler-Core'!
  2. (Smalltalk packageAt: 'Compiler-Core' ifAbsent: [ self error: 'Package not created: Compiler-Core' ]) imports: {'smalltalkParser' -> 'amber/parser'}!
  3. Object subclass: #AbstractCodeGenerator
  4. slots: {#currentClass. #currentPackage. #source}
  5. package: 'Compiler-Core'!
  6. !AbstractCodeGenerator commentStamp!
  7. I am the abstract super class of all code generators and provide their common API.!
  8. !AbstractCodeGenerator methodsFor: 'accessing'!
  9. currentClass
  10. ^ currentClass
  11. !
  12. currentClass: aClass
  13. currentClass := aClass
  14. !
  15. currentPackage
  16. ^ currentPackage
  17. !
  18. currentPackage: anObject
  19. currentPackage := anObject
  20. !
  21. pseudoVariables
  22. ^ Smalltalk pseudoVariableNames
  23. !
  24. source
  25. ^ source ifNil: [ '' ]
  26. !
  27. source: aString
  28. source := aString
  29. ! !
  30. !AbstractCodeGenerator methodsFor: 'compiling'!
  31. compileNode: aNode
  32. ^ self transformers
  33. inject: aNode
  34. into: [ :input :transformer | transformer value: input ]
  35. !
  36. transformers
  37. | dict |
  38. dict := self transformersDictionary.
  39. ^ dict keys asArray sort collect: [ :each | dict at: each ]
  40. !
  41. transformersDictionary
  42. self subclassResponsibility
  43. ! !
  44. AbstractCodeGenerator subclass: #CodeGenerator
  45. slots: {#transformersDictionary}
  46. package: 'Compiler-Core'!
  47. !CodeGenerator commentStamp!
  48. I am a basic code generator. I generate a valid JavaScript output, but no not perform any inlining.
  49. See `InliningCodeGenerator` for an optimized JavaScript code generation.!
  50. !CodeGenerator methodsFor: 'compiling'!
  51. earlyAstPragmator
  52. ^ AstEarlyPragmator new
  53. !
  54. irTranslator
  55. ^ self irTranslatorClass new
  56. currentClass: self currentClass;
  57. yourself
  58. !
  59. irTranslatorClass
  60. ^ IRJSTranslator
  61. !
  62. semanticAnalyzer
  63. ^ (SemanticAnalyzer on: self currentClass)
  64. thePackage: self currentPackage;
  65. yourself
  66. !
  67. transformersDictionary
  68. ^ transformersDictionary ifNil: [ transformersDictionary := Dictionary new
  69. at: '1000-earlyPragmas' put: self earlyAstPragmator;
  70. at: '2000-semantic' put: self semanticAnalyzer;
  71. at: '5000-astToIr' put: self translator;
  72. at: '8000-irToJs' put: self irTranslator;
  73. yourself ]
  74. !
  75. translator
  76. ^ IRASTTranslator new
  77. source: self source;
  78. theClass: self currentClass;
  79. yourself
  80. ! !
  81. Object subclass: #Compiler
  82. slots: {#currentPackage. #codeGeneratorClass. #codeGenerator}
  83. package: 'Compiler-Core'!
  84. !Compiler commentStamp!
  85. I provide the public interface for compiling Amber source code into JavaScript.
  86. The code generator used to produce JavaScript can be plugged with `#codeGeneratorClass`.
  87. The default code generator is an instance of `InlinedCodeGenerator`!
  88. !Compiler methodsFor: 'accessing'!
  89. cleanCodeGenerator
  90. codeGenerator := nil
  91. !
  92. codeGenerator
  93. ^ codeGenerator
  94. !
  95. codeGenerator: anObject
  96. codeGenerator := anObject
  97. !
  98. codeGeneratorClass
  99. ^ codeGeneratorClass ifNil: [ InliningCodeGenerator ]
  100. !
  101. codeGeneratorClass: aClass
  102. codeGeneratorClass := aClass
  103. !
  104. currentPackage
  105. ^ currentPackage
  106. !
  107. currentPackage: anObject
  108. currentPackage := anObject
  109. ! !
  110. !Compiler methodsFor: 'compiling'!
  111. ast: aString forClass: aClass protocol: anotherString
  112. self
  113. start: aString forClass: aClass protocol: anotherString;
  114. transformerAt: '2500-astCheckpoint' put: [ :x | ^x ];
  115. compileNode: (self parse: aString);
  116. error: 'AST transformation failed.'
  117. !
  118. compile: aString forClass: aClass protocol: anotherString
  119. | compilationResult result pragmas closureFactory |
  120. compilationResult := self
  121. start: aString forClass: aClass protocol: anotherString;
  122. compileNode: (self parse: aString).
  123. closureFactory := self
  124. eval: '(function ($methodClass){ return ', compilationResult compiledSource, '; })'
  125. forPackage: self currentPackage.
  126. result := Smalltalk core method: #{
  127. #selector -> compilationResult selector.
  128. #protocol -> anotherString.
  129. #source -> compilationResult source.
  130. #messageSends -> compilationResult messageSends asArray.
  131. #args -> compilationResult arguments asArray.
  132. #referencedClasses -> compilationResult classReferences asArray.
  133. } withFactory: closureFactory.
  134. result pragmas: compilationResult pragmas.
  135. ^ result
  136. !
  137. compileNode: aNode
  138. | result |
  139. result := self codeGenerator compileNode: aNode.
  140. self cleanCodeGenerator.
  141. ^ result
  142. !
  143. eval: aString
  144. <inlineJS: 'return eval(aString)'>
  145. !
  146. eval: aString forPackage: aPackage
  147. ^ aPackage
  148. ifNil: [ self eval: aString ]
  149. ifNotNil: [ aPackage eval: aString ]
  150. !
  151. evaluateExpression: aString
  152. "Unlike #eval: evaluate a Smalltalk expression and answer the returned object"
  153. ^ self evaluateExpression: aString on: DoIt new
  154. !
  155. evaluateExpression: aString on: anObject
  156. "Unlike #eval: evaluate a Smalltalk expression with anObject as the receiver and answer the returned object"
  157. | result method |
  158. method := self
  159. install: (self sourceForExpression: aString)
  160. forClass: anObject class
  161. protocol: '**xxxDoIt'.
  162. result := anObject xxxDoIt.
  163. anObject class removeCompiledMethod: method.
  164. ^ result
  165. !
  166. install: aString forClass: aBehavior protocol: anotherString
  167. | compiledMethod |
  168. compiledMethod := self compile: aString forClass: aBehavior protocol: anotherString.
  169. aBehavior addCompiledMethod: compiledMethod.
  170. ^ compiledMethod
  171. !
  172. parse: aString
  173. | result |
  174. [ result := self basicParse: aString ]
  175. tryCatch: [ :ex | (self parseError: ex parsing: aString) signal ].
  176. ^ result
  177. !
  178. parseExpression: aString
  179. ^ self parse: (self sourceForExpression: aString)
  180. !
  181. recompile: aClass
  182. aClass includingPossibleMetaDo: [ :eachSide |
  183. eachSide methodDictionary values
  184. do: [ :each | each origin = eachSide ifTrue: [
  185. self
  186. install: each source
  187. forClass: eachSide
  188. protocol: each protocol ] ]
  189. displayingProgress: 'Recompiling ', eachSide name ]
  190. !
  191. recompileAll
  192. Smalltalk classes
  193. do: [ :each | self recompile: each ]
  194. displayingProgress: 'Compiling all classes...'
  195. !
  196. sourceForExpression: aString
  197. ^ 'xxxDoIt ^ [ ', aString, ' ] value'
  198. !
  199. start: aString forClass: aClass protocol: anotherString
  200. | package |
  201. package := aClass packageOfProtocol: anotherString.
  202. self
  203. currentPackage: package;
  204. codeGenerator: (self codeGeneratorClass new
  205. source: aString;
  206. currentClass: aClass;
  207. currentPackage: package;
  208. yourself)
  209. !
  210. transformerAt: aString put: anObject
  211. self codeGenerator transformersDictionary at: aString put: anObject
  212. ! !
  213. !Compiler methodsFor: 'error handling'!
  214. error: aString
  215. CompilerError signal: aString
  216. !
  217. parseError: anException parsing: aString
  218. (anException basicAt: 'location')
  219. ifNil: [ ^ anException pass ]
  220. ifNotNil: [ :loc |
  221. ^ ParseError new
  222. messageText:
  223. 'Parse error on line ', loc start line ,
  224. ' column ' , loc start column ,
  225. ' : Unexpected character ', (anException basicAt: 'found');
  226. yourself ]
  227. ! !
  228. !Compiler methodsFor: 'private'!
  229. basicParse: aString
  230. ^ smalltalkParser parse: aString
  231. ! !
  232. !Compiler class methodsFor: 'compiling'!
  233. recompile: aClass
  234. self new recompile: aClass
  235. !
  236. recompileAll
  237. Smalltalk classes do: [ :each |
  238. self recompile: each ]
  239. ! !
  240. !Compiler class methodsFor: 'evaluating'!
  241. eval: aString
  242. ^ self new eval: aString
  243. ! !
  244. !Compiler class methodsFor: 'initialization'!
  245. initialize
  246. "TODO remove, backward compat"
  247. Smalltalk globals at: #SmalltalkParser put: smalltalkParser
  248. ! !
  249. !Compiler class methodsFor: 'parsing'!
  250. parse: aString
  251. ^ self new parse: aString
  252. ! !
  253. Object subclass: #DoIt
  254. slots: {}
  255. package: 'Compiler-Core'!
  256. !DoIt commentStamp!
  257. `DoIt` is the class used to compile and evaluate expressions. See `Compiler >> evaluateExpression:`.!
  258. Object subclass: #Evaluator
  259. slots: {}
  260. package: 'Compiler-Core'!
  261. !Evaluator commentStamp!
  262. I evaluate code against a receiver, dispatching #evaluate:on: to the receiver.!
  263. !Evaluator methodsFor: 'evaluating'!
  264. evaluate: aString context: aContext
  265. "Similar to #evaluate:for:, with the following differences:
  266. - instead of compiling and running `aString`, `aString` is interpreted using an `ASTInterpreter`
  267. - instead of evaluating against a receiver, evaluate in the context of `aContext`"
  268. | compiler ast |
  269. compiler := Compiler new.
  270. [ ast := compiler parseExpression: aString ]
  271. on: Error
  272. do: [ :ex | ^ Terminal alert: ex messageText ].
  273. (AISemanticAnalyzer on: aContext receiver class)
  274. context: aContext;
  275. visit: ast.
  276. ^ aContext evaluateNode: ast
  277. !
  278. evaluate: aString for: anObject
  279. ^ anObject evaluate: aString on: self
  280. !
  281. evaluate: aString receiver: anObject
  282. | compiler |
  283. compiler := Compiler new.
  284. [ compiler parseExpression: aString ]
  285. on: Error
  286. do: [ :ex | ^ Terminal alert: ex messageText ].
  287. ^ compiler evaluateExpression: aString on: anObject
  288. ! !
  289. !Evaluator class methodsFor: 'instance creation'!
  290. evaluate: aString for: anObject
  291. ^ self new evaluate: aString for: anObject
  292. ! !
  293. Error subclass: #ParseError
  294. slots: {}
  295. package: 'Compiler-Core'!
  296. !ParseError commentStamp!
  297. Instance of ParseError are signaled on any parsing error.
  298. See `Compiler >> #parse:`!
  299. !String methodsFor: '*Compiler-Core'!
  300. asVariableName
  301. ^ (Smalltalk reservedWords includes: self)
  302. ifTrue: [ self, '_' ]
  303. ifFalse: [ self ]
  304. ! !