Compiler-Semantic.st 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761
  1. Smalltalk createPackage: 'Compiler-Semantic'!
  2. Object subclass: #LexicalScope
  3. slots: {#node. #instruction. #temps. #args. #outerScope. #blockIndex}
  4. package: 'Compiler-Semantic'!
  5. !LexicalScope commentStamp!
  6. I represent a lexical scope where variable names are associated with ScopeVars
  7. Instances are used for block scopes. Method scopes are instances of MethodLexicalScope.
  8. I am attached to a ScopeVar and method/block nodes.
  9. Each context (method/closure) get a fresh scope that inherits from its outer scope.!
  10. !LexicalScope methodsFor: 'accessing'!
  11. alias
  12. ^ '$ctx', self scopeLevel asString
  13. !
  14. allVariableNames
  15. ^ self args keys, self temps keys
  16. !
  17. args
  18. ^ args ifNil: [ args := Dictionary new ]
  19. !
  20. bindingFor: aStringOrNode
  21. ^ self pseudoVars at: aStringOrNode value ifAbsent: [
  22. self args at: aStringOrNode value ifAbsent: [
  23. self temps at: aStringOrNode value ifAbsent: [ nil ]]]
  24. !
  25. blockIndex
  26. ^ blockIndex ifNil: [ 0 ]
  27. !
  28. blockIndex: anInteger
  29. blockIndex := anInteger
  30. !
  31. instruction
  32. ^ instruction
  33. !
  34. instruction: anIRInstruction
  35. instruction := anIRInstruction
  36. !
  37. lookupVariable: aNode
  38. | lookup |
  39. lookup := (self bindingFor: aNode).
  40. lookup ifNil: [
  41. lookup := self outerScope ifNotNil: [
  42. (self outerScope lookupVariable: aNode) ]].
  43. ^ lookup
  44. !
  45. methodScope
  46. ^ self outerScope ifNotNil: [
  47. self outerScope methodScope ]
  48. !
  49. node
  50. "Answer the node in which I am defined"
  51. ^ node
  52. !
  53. node: aNode
  54. node := aNode
  55. !
  56. outerScope
  57. ^ outerScope
  58. !
  59. outerScope: aLexicalScope
  60. outerScope := aLexicalScope
  61. !
  62. pseudoVars
  63. ^ self methodScope pseudoVars
  64. !
  65. scopeLevel
  66. self outerScope ifNil: [ ^ 1 ].
  67. self isInlined ifTrue: [ ^ self outerScope scopeLevel ].
  68. ^ self outerScope scopeLevel + 1
  69. !
  70. temps
  71. ^ temps ifNil: [ temps := Dictionary new ]
  72. ! !
  73. !LexicalScope methodsFor: 'adding'!
  74. addArg: aString
  75. self args at: aString put: (ArgVar on: aString).
  76. (self args at: aString) scope: self
  77. !
  78. addTemp: aString
  79. self temps at: aString put: (TempVar on: aString).
  80. (self temps at: aString) scope: self
  81. ! !
  82. !LexicalScope methodsFor: 'testing'!
  83. canInlineNonLocalReturns
  84. ^ self isInlined and: [ self outerScope canInlineNonLocalReturns ]
  85. !
  86. isBlockScope
  87. ^ self isMethodScope not
  88. !
  89. isInlined
  90. ^ self instruction notNil and: [
  91. self instruction isInlined ]
  92. !
  93. isMethodScope
  94. ^ false
  95. ! !
  96. LexicalScope subclass: #MethodLexicalScope
  97. slots: {#iVars. #pseudoVars. #unknownVariables. #localReturn. #nonLocalReturns}
  98. package: 'Compiler-Semantic'!
  99. !MethodLexicalScope commentStamp!
  100. I represent a method scope.!
  101. !MethodLexicalScope methodsFor: 'accessing'!
  102. allVariableNames
  103. ^ super allVariableNames, self iVars keys
  104. !
  105. bindingFor: aNode
  106. ^ (super bindingFor: aNode) ifNil: [
  107. self iVars at: aNode value ifAbsent: [ nil ]]
  108. !
  109. iVars
  110. ^ iVars ifNil: [ iVars := Dictionary new ]
  111. !
  112. localReturn
  113. ^ localReturn ifNil: [ false ]
  114. !
  115. localReturn: aBoolean
  116. localReturn := aBoolean
  117. !
  118. methodScope
  119. ^ self
  120. !
  121. nonLocalReturns
  122. ^ nonLocalReturns ifNil: [ nonLocalReturns := OrderedCollection new ]
  123. !
  124. pseudoVars
  125. pseudoVars ifNil: [
  126. pseudoVars := Dictionary new.
  127. PseudoVar dictionary keysAndValuesDo: [ :each :impl |
  128. pseudoVars at: each put: ((impl on: each)
  129. scope: self methodScope;
  130. yourself) ] ].
  131. ^ pseudoVars
  132. !
  133. unknownVariables
  134. ^ unknownVariables ifNil: [ unknownVariables := OrderedCollection new ]
  135. ! !
  136. !MethodLexicalScope methodsFor: 'adding'!
  137. addIVar: aString
  138. self iVars at: aString put: (InstanceVar on: aString).
  139. (self iVars at: aString) scope: self
  140. !
  141. addNonLocalReturn: aScope
  142. self nonLocalReturns add: aScope
  143. !
  144. removeNonLocalReturn: aScope
  145. self nonLocalReturns remove: aScope ifAbsent: []
  146. ! !
  147. !MethodLexicalScope methodsFor: 'testing'!
  148. canInlineNonLocalReturns
  149. ^ true
  150. !
  151. hasLocalReturn
  152. ^ self localReturn
  153. !
  154. hasNonLocalReturn
  155. ^ self nonLocalReturns notEmpty
  156. !
  157. isMethodScope
  158. ^ true
  159. ! !
  160. Object subclass: #ScopeVar
  161. slots: {#scope. #name}
  162. package: 'Compiler-Semantic'!
  163. !ScopeVar commentStamp!
  164. I am an entry in a LexicalScope that gets associated with variable nodes of the same name.
  165. There are 4 different subclasses of vars: temp vars, local vars, args, and unknown/global vars.!
  166. !ScopeVar methodsFor: 'accessing'!
  167. alias
  168. ^ self name asVariableName
  169. !
  170. name
  171. ^ name
  172. !
  173. name: aString
  174. name := aString
  175. !
  176. scope
  177. ^ scope
  178. !
  179. scope: aScope
  180. scope := aScope
  181. ! !
  182. !ScopeVar methodsFor: 'testing'!
  183. isArgVar
  184. ^ false
  185. !
  186. isClassRefVar
  187. ^ false
  188. !
  189. isImmutable
  190. ^ false
  191. !
  192. isInstanceVar
  193. ^ false
  194. !
  195. isPseudoVar
  196. ^ false
  197. !
  198. isSelf
  199. ^ false
  200. !
  201. isSuper
  202. ^ false
  203. !
  204. isTempVar
  205. ^ false
  206. !
  207. isUnknownVar
  208. ^ false
  209. !
  210. validateAssignment
  211. (self isArgVar or: [ self isPseudoVar ]) ifTrue: [
  212. InvalidAssignmentError new
  213. variableName: self name;
  214. signal]
  215. ! !
  216. !ScopeVar class methodsFor: 'instance creation'!
  217. on: aString
  218. ^ self new
  219. name: aString;
  220. yourself
  221. ! !
  222. ScopeVar subclass: #AliasVar
  223. slots: {#node}
  224. package: 'Compiler-Semantic'!
  225. !AliasVar commentStamp!
  226. I am an internally defined variable by the compiler!
  227. !AliasVar methodsFor: 'accessing'!
  228. node
  229. ^ node
  230. !
  231. node: aNode
  232. node := aNode
  233. ! !
  234. !AliasVar methodsFor: 'testing'!
  235. isImmutable
  236. ^ true
  237. ! !
  238. ScopeVar subclass: #ArgVar
  239. slots: {}
  240. package: 'Compiler-Semantic'!
  241. !ArgVar commentStamp!
  242. I am an argument of a method or block.!
  243. !ArgVar methodsFor: 'testing'!
  244. isArgVar
  245. ^ true
  246. !
  247. isImmutable
  248. ^ true
  249. ! !
  250. ScopeVar subclass: #ClassRefVar
  251. slots: {}
  252. package: 'Compiler-Semantic'!
  253. !ClassRefVar commentStamp!
  254. I am an class reference variable!
  255. !ClassRefVar methodsFor: 'accessing'!
  256. alias
  257. "Fixes issue #190.
  258. A function is created in the method definition, answering the class or nil.
  259. See JSStream >> #nextPutClassRefFunction:"
  260. ^ '$globals.', self name
  261. ! !
  262. !ClassRefVar methodsFor: 'testing'!
  263. isClassRefVar
  264. ^ true
  265. !
  266. isImmutable
  267. ^ true
  268. ! !
  269. ScopeVar subclass: #InstanceVar
  270. slots: {}
  271. package: 'Compiler-Semantic'!
  272. !InstanceVar commentStamp!
  273. I am an instance variable of a method or block.!
  274. !InstanceVar methodsFor: 'testing'!
  275. alias
  276. ^ '$self.', self name
  277. !
  278. isInstanceVar
  279. ^ true
  280. ! !
  281. ScopeVar subclass: #PseudoVar
  282. slots: {}
  283. package: 'Compiler-Semantic'!
  284. !PseudoVar commentStamp!
  285. I am an pseudo variable.
  286. The five Smalltalk pseudo variables are: 'self', 'super', 'nil', 'true' and 'false'!
  287. !PseudoVar methodsFor: 'accessing'!
  288. alias
  289. ^ self name
  290. ! !
  291. !PseudoVar methodsFor: 'testing'!
  292. isImmutable
  293. ^ true
  294. !
  295. isPseudoVar
  296. ^ true
  297. !
  298. isSelf
  299. ^ name = 'self'
  300. ! !
  301. PseudoVar class slots: {#dictionary}!
  302. !PseudoVar class methodsFor: 'accessing'!
  303. dictionary
  304. ^ dictionary ifNil: [ dictionary := Dictionary new
  305. at: #self put: PseudoVar;
  306. at: #super put: SuperVar;
  307. at: #nil put: PseudoVar;
  308. at: #false put: PseudoVar;
  309. at: #true put: PseudoVar;
  310. at: #thisContext put: ThisContextVar;
  311. yourself ]
  312. ! !
  313. PseudoVar subclass: #SuperVar
  314. slots: {}
  315. package: 'Compiler-Semantic'!
  316. !SuperVar commentStamp!
  317. I am a 'super' pseudo variable.!
  318. !SuperVar methodsFor: 'accessing'!
  319. alias
  320. ^ '$self'
  321. !
  322. lookupAsJavaScriptSource
  323. ^ '($methodClass.superclass||$boot.nilAsClass).fn.prototype'
  324. ! !
  325. !SuperVar methodsFor: 'testing'!
  326. isSuper
  327. ^ true
  328. ! !
  329. PseudoVar subclass: #ThisContextVar
  330. slots: {}
  331. package: 'Compiler-Semantic'!
  332. !ThisContextVar commentStamp!
  333. I am a 'thisContext' pseudo variable.!
  334. !ThisContextVar methodsFor: 'accessing'!
  335. alias
  336. ^ '$core.getThisContext()'
  337. ! !
  338. ScopeVar subclass: #TempVar
  339. slots: {}
  340. package: 'Compiler-Semantic'!
  341. !TempVar commentStamp!
  342. I am an temporary variable of a method or block.!
  343. !TempVar methodsFor: 'testing'!
  344. isTempVar
  345. ^ true
  346. ! !
  347. ScopeVar subclass: #UnknownVar
  348. slots: {}
  349. package: 'Compiler-Semantic'!
  350. !UnknownVar commentStamp!
  351. I am an unknown variable. Amber uses unknown variables as JavaScript globals!
  352. !UnknownVar methodsFor: 'testing'!
  353. isUnknownVar
  354. ^ true
  355. ! !
  356. NodeVisitor subclass: #SemanticAnalyzer
  357. slots: {#currentScope. #blockIndex. #thePackage. #theClass. #classReferences. #messageSends}
  358. package: 'Compiler-Semantic'!
  359. !SemanticAnalyzer commentStamp!
  360. I semantically analyze the abstract syntax tree and annotate it with informations such as non local returns and variable scopes.!
  361. !SemanticAnalyzer methodsFor: 'accessing'!
  362. classReferences
  363. ^ classReferences ifNil: [ classReferences := Set new ]
  364. !
  365. messageSends
  366. ^ messageSends ifNil: [ messageSends := Dictionary new ]
  367. !
  368. theClass
  369. ^ theClass
  370. !
  371. theClass: aClass
  372. theClass := aClass
  373. !
  374. thePackage
  375. ^ thePackage
  376. !
  377. thePackage: aPackage
  378. thePackage := aPackage
  379. ! !
  380. !SemanticAnalyzer methodsFor: 'error handling'!
  381. errorShadowingVariable: aString
  382. ShadowingVariableError new
  383. variableName: aString;
  384. signal
  385. !
  386. errorUnknownVariable: aNode
  387. "Throw an error if the variable is undeclared in the global JS scope (i.e. window).
  388. We allow all variables listed by Smalltalk>>#globalJsVariables.
  389. This list includes: `window`, `document`, `process` and `global`
  390. for nodejs and browser environments.
  391. This is only to make sure compilation works on both browser-based and nodejs environments.
  392. The ideal solution would be to use a pragma instead"
  393. | identifier |
  394. identifier := aNode value.
  395. ((Smalltalk globalJsVariables includes: identifier) not
  396. and: [ self isVariableUndefined: identifier inPackage: self thePackage ])
  397. ifTrue: [
  398. UnknownVariableError new
  399. variableName: aNode value;
  400. signal ]
  401. ifFalse: [
  402. currentScope methodScope unknownVariables add: aNode value ]
  403. ! !
  404. !SemanticAnalyzer methodsFor: 'factory'!
  405. newBlockScope
  406. ^ self newScopeOfClass: LexicalScope
  407. !
  408. newMethodScope
  409. ^ self newScopeOfClass: MethodLexicalScope
  410. !
  411. newScopeOfClass: aLexicalScopeClass
  412. ^ aLexicalScopeClass new
  413. outerScope: currentScope;
  414. yourself
  415. ! !
  416. !SemanticAnalyzer methodsFor: 'private'!
  417. nextBlockIndex
  418. blockIndex ifNil: [ blockIndex := 0 ].
  419. blockIndex := blockIndex + 1.
  420. ^ blockIndex
  421. ! !
  422. !SemanticAnalyzer methodsFor: 'scope'!
  423. popScope
  424. currentScope ifNotNil: [
  425. currentScope := currentScope outerScope ]
  426. !
  427. pushScope: aScope
  428. aScope outerScope: currentScope.
  429. currentScope := aScope
  430. !
  431. validateVariableScope: aString
  432. "Validate the variable scope in by doing a recursive lookup, up to the method scope"
  433. (currentScope lookupVariable: aString) ifNotNil: [
  434. self errorShadowingVariable: aString ]
  435. ! !
  436. !SemanticAnalyzer methodsFor: 'testing'!
  437. isVariableUndefined: aString inPackage: aPackage
  438. aPackage ifNotNil: [
  439. | packageKnownVars |
  440. packageKnownVars := (aPackage imports
  441. reject: #isString)
  442. collect: #key.
  443. (packageKnownVars includes: aString) ifTrue: [ ^ false ]].
  444. ^ Compiler eval: 'typeof ', aString, ' === "undefined"'
  445. ! !
  446. !SemanticAnalyzer methodsFor: 'visiting'!
  447. visitAssignmentNode: aNode
  448. super visitAssignmentNode: aNode.
  449. aNode left beAssigned
  450. !
  451. visitBlockNode: aNode
  452. self pushScope: self newBlockScope.
  453. aNode scope: currentScope.
  454. currentScope node: aNode.
  455. currentScope blockIndex: self nextBlockIndex.
  456. aNode parameters do: [ :each |
  457. self validateVariableScope: each.
  458. currentScope addArg: each ].
  459. super visitBlockNode: aNode.
  460. self popScope
  461. !
  462. visitCascadeNode: aNode
  463. aNode receiver: aNode dagChildren first receiver.
  464. super visitCascadeNode: aNode
  465. !
  466. visitMethodNode: aNode
  467. self pushScope: self newMethodScope.
  468. aNode scope: currentScope.
  469. currentScope node: aNode.
  470. self theClass allInstanceVariableNames do: [ :each |
  471. currentScope addIVar: each ].
  472. aNode arguments do: [ :each |
  473. self validateVariableScope: each.
  474. currentScope addArg: each ].
  475. super visitMethodNode: aNode.
  476. aNode
  477. classReferences: self classReferences;
  478. sendIndexes: self messageSends.
  479. self popScope.
  480. ^ aNode
  481. !
  482. visitReturnNode: aNode
  483. aNode scope: currentScope.
  484. currentScope isMethodScope
  485. ifTrue: [ currentScope localReturn: true ]
  486. ifFalse: [ currentScope methodScope addNonLocalReturn: currentScope ].
  487. super visitReturnNode: aNode
  488. !
  489. visitSendNode: aNode
  490. | sends |
  491. sends := self messageSends at: aNode selector ifAbsentPut: [ OrderedCollection new ].
  492. sends add: aNode.
  493. aNode index: sends size.
  494. super visitSendNode: aNode
  495. !
  496. visitSequenceNode: aNode
  497. aNode temps do: [ :each |
  498. self validateVariableScope: each.
  499. currentScope addTemp: each ].
  500. super visitSequenceNode: aNode
  501. !
  502. visitVariableNode: aNode
  503. "Bind a ScopeVar to aNode by doing a lookup in the current scope.
  504. If no ScopeVar is found, bind a UnknowVar and throw an error."
  505. | binding |
  506. binding := currentScope lookupVariable: aNode.
  507. binding ifNil: [
  508. aNode value isCapitalized
  509. ifTrue: [ "Capital letter variables might be globals."
  510. binding := ClassRefVar new name: aNode value; yourself.
  511. self classReferences add: aNode value]
  512. ifFalse: [
  513. self errorUnknownVariable: aNode.
  514. binding := UnknownVar new name: aNode value; yourself ] ].
  515. aNode binding: binding.
  516. ! !
  517. !SemanticAnalyzer class methodsFor: 'instance creation'!
  518. on: aClass
  519. ^ self new
  520. theClass: aClass;
  521. yourself
  522. ! !
  523. CompilerError subclass: #SemanticError
  524. slots: {}
  525. package: 'Compiler-Semantic'!
  526. !SemanticError commentStamp!
  527. I represent an abstract semantic error thrown by the SemanticAnalyzer.
  528. Semantic errors can be unknown variable errors, etc.
  529. See my subclasses for concrete errors.
  530. The IDE should catch instances of Semantic error to deal with them when compiling!
  531. SemanticError subclass: #InvalidAssignmentError
  532. slots: {#variableName}
  533. package: 'Compiler-Semantic'!
  534. !InvalidAssignmentError commentStamp!
  535. I get signaled when a pseudo variable gets assigned.!
  536. !InvalidAssignmentError methodsFor: 'accessing'!
  537. messageText
  538. ^ ' Invalid assignment to variable: ', self variableName
  539. !
  540. variableName
  541. ^ variableName
  542. !
  543. variableName: aString
  544. variableName := aString
  545. ! !
  546. SemanticError subclass: #ShadowingVariableError
  547. slots: {#variableName}
  548. package: 'Compiler-Semantic'!
  549. !ShadowingVariableError commentStamp!
  550. I get signaled when a variable in a block or method scope shadows a variable of the same name in an outer scope.!
  551. !ShadowingVariableError methodsFor: 'accessing'!
  552. messageText
  553. ^ 'Variable shadowing error: ', self variableName, ' is already defined'
  554. !
  555. variableName
  556. ^ variableName
  557. !
  558. variableName: aString
  559. variableName := aString
  560. ! !
  561. SemanticError subclass: #UnknownVariableError
  562. slots: {#variableName}
  563. package: 'Compiler-Semantic'!
  564. !UnknownVariableError commentStamp!
  565. I get signaled when a variable is not defined.
  566. The default behavior is to allow it, as this is how Amber currently is able to seamlessly send messages to JavaScript objects.!
  567. !UnknownVariableError methodsFor: 'accessing'!
  568. messageText
  569. ^ 'Unknown Variable error: ', self variableName, ' is not defined'
  570. !
  571. variableName
  572. ^ variableName
  573. !
  574. variableName: aString
  575. variableName := aString
  576. ! !