Compiler-Semantic.st 14 KB

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