Compiler-Semantic.st 14 KB

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