Compiler-Semantic.st 14 KB

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