Compiler-Core.st 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  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. pragmas := compilationResult removeKey: #pragmas.
  124. closureFactory := self
  125. eval: (compilationResult removeKey: #instantiateFn)
  126. forPackage: self currentPackage.
  127. result := Smalltalk core method: compilationResult withFactory: closureFactory.
  128. result protocol: anotherString; pragmas: pragmas.
  129. ^ result
  130. !
  131. compileNode: aNode
  132. | result |
  133. result := self codeGenerator compileNode: aNode.
  134. self cleanCodeGenerator.
  135. ^ result
  136. !
  137. eval: aString
  138. <inlineJS: 'return eval(aString)'>
  139. !
  140. eval: aString forPackage: aPackage
  141. ^ aPackage
  142. ifNil: [ self eval: aString ]
  143. ifNotNil: [ aPackage eval: aString ]
  144. !
  145. evaluateExpression: aString
  146. "Unlike #eval: evaluate a Smalltalk expression and answer the returned object"
  147. ^ self evaluateExpression: aString on: DoIt new
  148. !
  149. evaluateExpression: aString on: anObject
  150. "Unlike #eval: evaluate a Smalltalk expression with anObject as the receiver and answer the returned object"
  151. | result method |
  152. method := self
  153. install: (self sourceForExpression: aString)
  154. forClass: anObject class
  155. protocol: '**xxxDoIt'.
  156. result := anObject xxxDoIt.
  157. anObject class removeCompiledMethod: method.
  158. ^ result
  159. !
  160. install: aString forClass: aBehavior protocol: anotherString
  161. | compiledMethod |
  162. compiledMethod := self compile: aString forClass: aBehavior protocol: anotherString.
  163. aBehavior addCompiledMethod: compiledMethod.
  164. ^ compiledMethod
  165. !
  166. parse: aString
  167. | result |
  168. [ result := self basicParse: aString ]
  169. tryCatch: [ :ex | (self parseError: ex parsing: aString) signal ].
  170. ^ result
  171. !
  172. parseExpression: aString
  173. ^ self parse: (self sourceForExpression: aString)
  174. !
  175. recompile: aClass
  176. aClass includingPossibleMetaDo: [ :eachSide |
  177. eachSide methodDictionary values
  178. do: [ :each | each origin = eachSide ifTrue: [
  179. self
  180. install: each source
  181. forClass: eachSide
  182. protocol: each protocol ] ]
  183. displayingProgress: 'Recompiling ', eachSide name ]
  184. !
  185. recompileAll
  186. Smalltalk classes
  187. do: [ :each | self recompile: each ]
  188. displayingProgress: 'Compiling all classes...'
  189. !
  190. sourceForExpression: aString
  191. ^ 'xxxDoIt ^ [ ', aString, ' ] value'
  192. !
  193. start: aString forClass: aClass protocol: anotherString
  194. | package |
  195. package := aClass packageOfProtocol: anotherString.
  196. self
  197. currentPackage: package;
  198. codeGenerator: (self codeGeneratorClass new
  199. source: aString;
  200. currentClass: aClass;
  201. currentPackage: package;
  202. yourself)
  203. !
  204. transformerAt: aString put: anObject
  205. self codeGenerator transformersDictionary at: aString put: anObject
  206. ! !
  207. !Compiler methodsFor: 'error handling'!
  208. error: aString
  209. CompilerError signal: aString
  210. !
  211. parseError: anException parsing: aString
  212. (anException basicAt: 'location')
  213. ifNil: [ ^ anException pass ]
  214. ifNotNil: [ :loc |
  215. ^ ParseError new
  216. messageText:
  217. 'Parse error on line ', loc start line ,
  218. ' column ' , loc start column ,
  219. ' : Unexpected character ', (anException basicAt: 'found');
  220. yourself ]
  221. ! !
  222. !Compiler methodsFor: 'private'!
  223. basicParse: aString
  224. ^ smalltalkParser parse: aString
  225. ! !
  226. !Compiler class methodsFor: 'compiling'!
  227. recompile: aClass
  228. self new recompile: aClass
  229. !
  230. recompileAll
  231. Smalltalk classes do: [ :each |
  232. self recompile: each ]
  233. ! !
  234. !Compiler class methodsFor: 'evaluating'!
  235. eval: aString
  236. ^ self new eval: aString
  237. ! !
  238. !Compiler class methodsFor: 'initialization'!
  239. initialize
  240. "TODO remove, backward compat"
  241. Smalltalk globals at: #SmalltalkParser put: smalltalkParser
  242. ! !
  243. !Compiler class methodsFor: 'parsing'!
  244. parse: aString
  245. ^ self new parse: aString
  246. ! !
  247. Object subclass: #DoIt
  248. slots: {}
  249. package: 'Compiler-Core'!
  250. !DoIt commentStamp!
  251. `DoIt` is the class used to compile and evaluate expressions. See `Compiler >> evaluateExpression:`.!
  252. Object subclass: #Evaluator
  253. slots: {}
  254. package: 'Compiler-Core'!
  255. !Evaluator commentStamp!
  256. I evaluate code against a receiver, dispatching #evaluate:on: to the receiver.!
  257. !Evaluator methodsFor: 'evaluating'!
  258. evaluate: aString context: aContext
  259. "Similar to #evaluate:for:, with the following differences:
  260. - instead of compiling and running `aString`, `aString` is interpreted using an `ASTInterpreter`
  261. - instead of evaluating against a receiver, evaluate in the context of `aContext`"
  262. | compiler ast |
  263. compiler := Compiler new.
  264. [ ast := compiler parseExpression: aString ]
  265. on: Error
  266. do: [ :ex | ^ Terminal alert: ex messageText ].
  267. (AISemanticAnalyzer on: aContext receiver class)
  268. context: aContext;
  269. visit: ast.
  270. ^ aContext evaluateNode: ast
  271. !
  272. evaluate: aString for: anObject
  273. ^ anObject evaluate: aString on: self
  274. !
  275. evaluate: aString receiver: anObject
  276. | compiler |
  277. compiler := Compiler new.
  278. [ compiler parseExpression: aString ]
  279. on: Error
  280. do: [ :ex | ^ Terminal alert: ex messageText ].
  281. ^ compiler evaluateExpression: aString on: anObject
  282. ! !
  283. !Evaluator class methodsFor: 'instance creation'!
  284. evaluate: aString for: anObject
  285. ^ self new evaluate: aString for: anObject
  286. ! !
  287. Error subclass: #ParseError
  288. slots: {}
  289. package: 'Compiler-Core'!
  290. !ParseError commentStamp!
  291. Instance of ParseError are signaled on any parsing error.
  292. See `Compiler >> #parse:`!
  293. !String methodsFor: '*Compiler-Core'!
  294. asVariableName
  295. ^ (Smalltalk reservedWords includes: self)
  296. ifTrue: [ self, '_' ]
  297. ifFalse: [ self ]
  298. ! !