Compiler-Semantic.st 14 KB

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