Compiler-Semantic.st 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759
  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. #unknownVariables. #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. unknownVariables
  136. ^ unknownVariables ifNil: [ unknownVariables := OrderedCollection new ]
  137. ! !
  138. !MethodLexicalScope methodsFor: 'adding'!
  139. addIVar: aString
  140. self iVars at: aString put: (InstanceVar on: aString).
  141. (self iVars at: aString) scope: self
  142. !
  143. addNonLocalReturn: aScope
  144. self nonLocalReturns add: aScope
  145. !
  146. removeNonLocalReturn: aScope
  147. self nonLocalReturns remove: aScope ifAbsent: []
  148. ! !
  149. !MethodLexicalScope methodsFor: 'testing'!
  150. canFlattenNonLocalReturns
  151. ^ true
  152. !
  153. hasLocalReturn
  154. ^ self localReturn
  155. !
  156. hasNonLocalReturn
  157. ^ self nonLocalReturns notEmpty
  158. !
  159. isMethodScope
  160. ^ true
  161. ! !
  162. Object subclass: #ScopeVar
  163. slots: {#scope. #name}
  164. package: 'Compiler-Semantic'!
  165. !ScopeVar commentStamp!
  166. I am an entry in a LexicalScope that gets associated with variable nodes of the same name.
  167. There are 4 different subclasses of vars: temp vars, local vars, args, and unknown/global vars.!
  168. !ScopeVar methodsFor: 'accessing'!
  169. alias
  170. ^ self name asVariableName
  171. !
  172. name
  173. ^ name
  174. !
  175. name: aString
  176. name := aString
  177. !
  178. scope
  179. ^ scope
  180. !
  181. scope: aScope
  182. scope := aScope
  183. ! !
  184. !ScopeVar methodsFor: 'testing'!
  185. isArgVar
  186. ^ false
  187. !
  188. isClassRefVar
  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. isUnknownVar
  210. ^ false
  211. !
  212. validateAssignment
  213. (self isArgVar or: [ self isPseudoVar ]) ifTrue: [
  214. InvalidAssignmentError new
  215. variableName: self name;
  216. signal]
  217. ! !
  218. !ScopeVar class methodsFor: 'instance creation'!
  219. on: aString
  220. ^ self new
  221. name: aString;
  222. yourself
  223. ! !
  224. ScopeVar subclass: #AliasVar
  225. slots: {#node}
  226. package: 'Compiler-Semantic'!
  227. !AliasVar commentStamp!
  228. I am an internally defined variable by the compiler!
  229. !AliasVar methodsFor: 'accessing'!
  230. node
  231. ^ node
  232. !
  233. node: aNode
  234. node := aNode
  235. ! !
  236. !AliasVar methodsFor: 'testing'!
  237. isImmutable
  238. ^ true
  239. ! !
  240. ScopeVar subclass: #ArgVar
  241. slots: {}
  242. package: 'Compiler-Semantic'!
  243. !ArgVar commentStamp!
  244. I am an argument of a method or block.!
  245. !ArgVar methodsFor: 'testing'!
  246. isArgVar
  247. ^ true
  248. !
  249. isImmutable
  250. ^ true
  251. ! !
  252. ScopeVar subclass: #ClassRefVar
  253. slots: {}
  254. package: 'Compiler-Semantic'!
  255. !ClassRefVar commentStamp!
  256. I am an class reference variable!
  257. !ClassRefVar methodsFor: 'accessing'!
  258. alias
  259. ^ '$globals.', self name
  260. ! !
  261. !ClassRefVar methodsFor: 'testing'!
  262. isClassRefVar
  263. ^ true
  264. !
  265. isImmutable
  266. ^ true
  267. ! !
  268. ScopeVar subclass: #InstanceVar
  269. slots: {}
  270. package: 'Compiler-Semantic'!
  271. !InstanceVar commentStamp!
  272. I am an instance variable of a method or block.!
  273. !InstanceVar methodsFor: 'testing'!
  274. alias
  275. ^ '$self.', self name
  276. !
  277. isInstanceVar
  278. ^ true
  279. ! !
  280. ScopeVar subclass: #PseudoVar
  281. slots: {}
  282. package: 'Compiler-Semantic'!
  283. !PseudoVar commentStamp!
  284. I am an pseudo variable.
  285. The five Smalltalk pseudo variables are: 'self', 'super', 'nil', 'true' and 'false'!
  286. !PseudoVar methodsFor: 'accessing'!
  287. alias
  288. ^ self name
  289. ! !
  290. !PseudoVar methodsFor: 'testing'!
  291. isImmutable
  292. ^ true
  293. !
  294. isPseudoVar
  295. ^ true
  296. !
  297. isSelf
  298. ^ name = 'self'
  299. ! !
  300. PseudoVar class slots: {#dictionary}!
  301. !PseudoVar class methodsFor: 'accessing'!
  302. dictionary
  303. ^ dictionary ifNil: [ dictionary := Dictionary new
  304. at: #self put: PseudoVar;
  305. at: #super put: SuperVar;
  306. at: #nil put: PseudoVar;
  307. at: #false put: PseudoVar;
  308. at: #true put: PseudoVar;
  309. at: #thisContext put: ThisContextVar;
  310. yourself ]
  311. ! !
  312. PseudoVar subclass: #SuperVar
  313. slots: {}
  314. package: 'Compiler-Semantic'!
  315. !SuperVar commentStamp!
  316. I am a 'super' pseudo variable.!
  317. !SuperVar methodsFor: 'accessing'!
  318. alias
  319. ^ '$self'
  320. !
  321. lookupAsJavaScriptSource
  322. ^ '($methodClass.superclass||$boot.nilAsClass).fn.prototype'
  323. ! !
  324. !SuperVar methodsFor: 'testing'!
  325. isSuper
  326. ^ true
  327. ! !
  328. PseudoVar subclass: #ThisContextVar
  329. slots: {}
  330. package: 'Compiler-Semantic'!
  331. !ThisContextVar commentStamp!
  332. I am a 'thisContext' pseudo variable.!
  333. !ThisContextVar methodsFor: 'accessing'!
  334. alias
  335. ^ '$core.getThisContext()'
  336. ! !
  337. ScopeVar subclass: #TempVar
  338. slots: {}
  339. package: 'Compiler-Semantic'!
  340. !TempVar commentStamp!
  341. I am an temporary variable of a method or block.!
  342. !TempVar methodsFor: 'testing'!
  343. isTempVar
  344. ^ true
  345. ! !
  346. ScopeVar subclass: #UnknownVar
  347. slots: {}
  348. package: 'Compiler-Semantic'!
  349. !UnknownVar commentStamp!
  350. I am an unknown variable. Amber uses unknown variables as JavaScript globals!
  351. !UnknownVar methodsFor: 'testing'!
  352. isUnknownVar
  353. ^ true
  354. ! !
  355. NodeVisitor subclass: #SemanticAnalyzer
  356. slots: {#currentScope. #blockIndex. #thePackage. #theClass. #classReferences. #messageSends}
  357. package: 'Compiler-Semantic'!
  358. !SemanticAnalyzer commentStamp!
  359. I semantically analyze the abstract syntax tree and annotate it with informations such as non local returns and variable scopes.!
  360. !SemanticAnalyzer methodsFor: 'accessing'!
  361. classReferences
  362. ^ classReferences ifNil: [ classReferences := Set new ]
  363. !
  364. messageSends
  365. ^ messageSends ifNil: [ messageSends := Dictionary new ]
  366. !
  367. theClass
  368. ^ theClass
  369. !
  370. theClass: aClass
  371. theClass := aClass
  372. !
  373. thePackage
  374. ^ thePackage
  375. !
  376. thePackage: aPackage
  377. thePackage := aPackage
  378. ! !
  379. !SemanticAnalyzer methodsFor: 'error handling'!
  380. errorShadowingVariable: aString
  381. ShadowingVariableError new
  382. variableName: aString;
  383. signal
  384. !
  385. errorUnknownVariable: aNode
  386. "Throw an error if the variable is undeclared in the global JS scope (i.e. window).
  387. We allow all variables listed by Smalltalk>>#globalJsVariables.
  388. This list includes: `window`, `document`, `process` and `global`
  389. for nodejs and browser environments.
  390. This is only to make sure compilation works on both browser-based and nodejs environments.
  391. The ideal solution would be to use a pragma instead"
  392. | identifier |
  393. identifier := aNode value.
  394. ((Smalltalk globalJsVariables includes: identifier) not
  395. and: [ self isVariableUndefined: identifier inPackage: self thePackage ])
  396. ifTrue: [
  397. UnknownVariableError new
  398. variableName: aNode value;
  399. signal ]
  400. ifFalse: [
  401. currentScope methodScope unknownVariables add: aNode value ]
  402. ! !
  403. !SemanticAnalyzer methodsFor: 'factory'!
  404. newBlockScope
  405. ^ self newScopeOfClass: LexicalScope
  406. !
  407. newMethodScope
  408. ^ self newScopeOfClass: MethodLexicalScope
  409. !
  410. newScopeOfClass: aLexicalScopeClass
  411. ^ aLexicalScopeClass new
  412. outerScope: currentScope;
  413. yourself
  414. ! !
  415. !SemanticAnalyzer methodsFor: 'private'!
  416. nextBlockIndex
  417. blockIndex ifNil: [ blockIndex := 0 ].
  418. blockIndex := blockIndex + 1.
  419. ^ blockIndex
  420. ! !
  421. !SemanticAnalyzer methodsFor: 'scope'!
  422. popScope
  423. currentScope ifNotNil: [
  424. currentScope := currentScope outerScope ]
  425. !
  426. pushScope: aScope
  427. aScope outerScope: currentScope.
  428. currentScope := aScope
  429. !
  430. validateVariableScope: aString
  431. "Validate the variable scope in by doing a recursive lookup, up to the method scope"
  432. (currentScope lookupVariable: aString) ifNotNil: [
  433. self errorShadowingVariable: aString ]
  434. ! !
  435. !SemanticAnalyzer methodsFor: 'testing'!
  436. isVariableUndefined: aString inPackage: aPackage
  437. aPackage ifNotNil: [
  438. | packageKnownVars |
  439. packageKnownVars := (aPackage imports
  440. reject: #isString)
  441. collect: #key.
  442. (packageKnownVars includes: aString) ifTrue: [ ^ false ]].
  443. ^ Compiler eval: 'typeof ', aString, ' === "undefined"'
  444. ! !
  445. !SemanticAnalyzer methodsFor: 'visiting'!
  446. visitAssignmentNode: aNode
  447. super visitAssignmentNode: aNode.
  448. aNode left beAssigned
  449. !
  450. visitBlockNode: aNode
  451. self pushScope: self newBlockScope.
  452. aNode scope: currentScope.
  453. currentScope node: aNode.
  454. currentScope blockIndex: self nextBlockIndex.
  455. aNode parameters do: [ :each |
  456. self validateVariableScope: each.
  457. currentScope addArg: each ].
  458. super visitBlockNode: aNode.
  459. self popScope
  460. !
  461. visitCascadeNode: aNode
  462. aNode receiver: aNode dagChildren first receiver.
  463. super visitCascadeNode: aNode
  464. !
  465. visitMethodNode: aNode
  466. self pushScope: self newMethodScope.
  467. aNode scope: currentScope.
  468. currentScope node: aNode.
  469. self theClass allInstanceVariableNames do: [ :each |
  470. currentScope addIVar: each ].
  471. aNode arguments do: [ :each |
  472. self validateVariableScope: each.
  473. currentScope addArg: each ].
  474. super visitMethodNode: aNode.
  475. aNode
  476. classReferences: self classReferences;
  477. sendIndexes: self messageSends.
  478. self popScope.
  479. ^ aNode
  480. !
  481. visitReturnNode: aNode
  482. aNode scope: currentScope.
  483. currentScope isMethodScope
  484. ifTrue: [ currentScope localReturn: true ]
  485. ifFalse: [ currentScope methodScope addNonLocalReturn: currentScope ].
  486. super visitReturnNode: aNode
  487. !
  488. visitSendNode: aNode
  489. | sends |
  490. sends := self messageSends at: aNode selector ifAbsentPut: [ OrderedCollection new ].
  491. sends add: aNode.
  492. aNode index: sends size.
  493. super visitSendNode: aNode
  494. !
  495. visitSequenceNode: aNode
  496. aNode temps do: [ :each |
  497. self validateVariableScope: each.
  498. currentScope addTemp: each ].
  499. super visitSequenceNode: aNode
  500. !
  501. visitVariableNode: aNode
  502. "Bind a ScopeVar to aNode by doing a lookup in the current scope.
  503. If no ScopeVar is found, bind a UnknowVar and throw an error."
  504. | binding |
  505. binding := currentScope lookupVariable: aNode.
  506. binding ifNil: [
  507. aNode value isCapitalized
  508. ifTrue: [ "Capital letter variables might be globals."
  509. binding := ClassRefVar new name: aNode value; yourself.
  510. self classReferences add: aNode value]
  511. ifFalse: [
  512. self errorUnknownVariable: aNode.
  513. binding := UnknownVar new name: aNode value; yourself ] ].
  514. aNode binding: binding.
  515. ! !
  516. !SemanticAnalyzer class methodsFor: 'instance creation'!
  517. on: aClass
  518. ^ self new
  519. theClass: aClass;
  520. yourself
  521. ! !
  522. CompilerError subclass: #SemanticError
  523. slots: {}
  524. package: 'Compiler-Semantic'!
  525. !SemanticError commentStamp!
  526. I represent an abstract semantic error thrown by the SemanticAnalyzer.
  527. Semantic errors can be unknown variable errors, etc.
  528. See my subclasses for concrete errors.
  529. The IDE should catch instances of Semantic error to deal with them when compiling!
  530. SemanticError subclass: #InvalidAssignmentError
  531. slots: {#variableName}
  532. package: 'Compiler-Semantic'!
  533. !InvalidAssignmentError commentStamp!
  534. I get signaled when a pseudo variable gets assigned.!
  535. !InvalidAssignmentError methodsFor: 'accessing'!
  536. messageText
  537. ^ ' Invalid assignment to variable: ', self variableName
  538. !
  539. variableName
  540. ^ variableName
  541. !
  542. variableName: aString
  543. variableName := aString
  544. ! !
  545. SemanticError subclass: #ShadowingVariableError
  546. slots: {#variableName}
  547. package: 'Compiler-Semantic'!
  548. !ShadowingVariableError commentStamp!
  549. I get signaled when a variable in a block or method scope shadows a variable of the same name in an outer scope.!
  550. !ShadowingVariableError methodsFor: 'accessing'!
  551. messageText
  552. ^ 'Variable shadowing error: ', self variableName, ' is already defined'
  553. !
  554. variableName
  555. ^ variableName
  556. !
  557. variableName: aString
  558. variableName := aString
  559. ! !
  560. SemanticError subclass: #UnknownVariableError
  561. slots: {#variableName}
  562. package: 'Compiler-Semantic'!
  563. !UnknownVariableError commentStamp!
  564. I get signaled when a variable is not defined.
  565. The default behavior is to allow it, as this is how Amber currently is able to seamlessly send messages to JavaScript objects.!
  566. !UnknownVariableError methodsFor: 'accessing'!
  567. messageText
  568. ^ 'Unknown Variable error: ', self variableName, ' is not defined'
  569. !
  570. variableName
  571. ^ variableName
  572. !
  573. variableName: aString
  574. variableName := aString
  575. ! !