Compiler-Core.st 8.9 KB

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