Skip to content

Commit

Permalink
Fix #193 (#218)
Browse files Browse the repository at this point in the history
Co-authored-by: Nahuel Palumbo <[email protected]>
  • Loading branch information
fdodino and PalumboN committed Feb 28, 2024
1 parent 8b703f5 commit 2431dec
Show file tree
Hide file tree
Showing 7 changed files with 34 additions and 24 deletions.
1 change: 1 addition & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export const WOLLOK_EXTRA_STACK_TRACE_HEADER = 'Derived from TypeScript stack'
export const WOLLOK_BASE_PACKAGE = 'wollok.'

export const INITIALIZE_METHOD_NAME = 'initialize'
export const CLOSURE_METHOD_NAME = '<apply>'

export const PREFIX_OPERATORS: Record<Name, Name> = {
'!': 'negate',
Expand Down
4 changes: 2 additions & 2 deletions src/interpreter/runtimeModel.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { v4 as uuid } from 'uuid'
import { INITIALIZE_METHOD_NAME, LIST_MODULE, SET_MODULE, WOLLOK_BASE_PACKAGE, WOLLOK_EXTRA_STACK_TRACE_HEADER } from '../constants'
import { CLOSURE_METHOD_NAME, INITIALIZE_METHOD_NAME, LIST_MODULE, SET_MODULE, WOLLOK_BASE_PACKAGE, WOLLOK_EXTRA_STACK_TRACE_HEADER } from '../constants'
import { getPotentiallyUninitializedLazy } from '../decorators'
import { get, is, last, List, match, raise, when } from '../extensions'
import { Assignment, Body, Catch, Class, Describe, Entity, Environment, Expression, Field, Id, If, Literal, LiteralValue, Method, Module, Name, New, Node, Package, Program, Reference, Return, Self, Send, Singleton, Super, Test, Throw, Try, Variable } from '../model'
Expand Down Expand Up @@ -141,7 +141,7 @@ export class Frame extends Context {
// TODO: On error report, this tells the node line, but not the actual error line.
// For example, an error on a test would say the test start line, not the line where the error occurred.
get sourceInfo(): string {
const target = this.node.is(Method) && this.node.name === '<apply>'
const target = this.node.is(Method) && this.node.name === CLOSURE_METHOD_NAME
? this.node.parent
: this.node
return target.sourceInfo
Expand Down
8 changes: 4 additions & 4 deletions src/model.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { INFIX_OPERATORS, PREFIX_OPERATORS, WOLLOK_BASE_PACKAGE } from './constants'
import { CLOSURE_METHOD_NAME, INFIX_OPERATORS, PREFIX_OPERATORS, WOLLOK_BASE_PACKAGE } from './constants'
import { cached, getPotentiallyUninitializedLazy, lazy } from './decorators'
import { ConstructorFor, InstanceOf, is, last, List, mapObject, Mixable, MixinDefinition, MIXINS, isEmpty, notEmpty, TypeDefinition } from './extensions'
import { TypeRegistry, WollokType } from './typeSystem/wollokTypes'
Expand Down Expand Up @@ -551,8 +551,8 @@ export class Singleton extends Expression(Module(Node)) {
@cached
isClosure(arity?: number): boolean {
return arity === undefined
? this.methods.some(_ => _.name === '<apply>')
: !!this.lookupMethod('<apply>', arity)
? this.methods.some(_ => _.name === CLOSURE_METHOD_NAME)
: !!this.lookupMethod(CLOSURE_METHOD_NAME, arity)
}
}

Expand Down Expand Up @@ -862,7 +862,7 @@ export const Closure = ({ sentences, parameters, code, ...payload }: ClosurePayl
return new Singleton({
supertypes: [new ParameterizedType({ reference: new Reference({ name: 'wollok.lang.Closure' }) })],
members: [
new Method({ name: '<apply>', parameters, body: new Body({ sentences: [...initialSentences, ...lastSentence] }) }),
new Method({ name: CLOSURE_METHOD_NAME, parameters, body: new Body({ sentences: [...initialSentences, ...lastSentence] }) }),
...code ? [
new Field({ name: '<toString>', isConstant: true, value: new Literal({ value: code }) }),
] : [],
Expand Down
3 changes: 2 additions & 1 deletion src/typeSystem/constraintBasedTypeSystem.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { CLOSURE_METHOD_NAME } from '../constants'
import { anyPredicate, is, isEmpty, notEmpty } from '../extensions'
import { Environment, Module, Node, Reference } from '../model'
import { newTypeVariables, TypeVariable, typeVariableFor } from './typeVariables'
Expand Down Expand Up @@ -80,7 +81,7 @@ export const bindReceivedMessages = (tVar: TypeVariable): boolean => {
let changed = false
for (const type of types) {
for (const send of tVar.messages) {
const message = send.message == 'apply' ? '<apply>' : send.message // 'apply' is a special case for closures
const message = send.message == 'apply' ? CLOSURE_METHOD_NAME : send.message // 'apply' is a special case for closures
const method = type.lookupMethod(message, send.args.length, { allowAbstractMethods: true })
if (!method)
return reportProblem(tVar, new TypeSystemProblem('methodNotFound', [send.signature, type.name]))
Expand Down
3 changes: 2 additions & 1 deletion src/typeSystem/typeVariables.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { CLOSURE_METHOD_NAME } from '../constants'
import { is, last, List, match, when } from '../extensions'
import { Assignment, Body, Class, Closure, Describe, Environment, Expression, Field, If, Import, Literal, Method, Module, NamedArgument, New, Node, Package, Parameter, Program, Reference, Return, Self, Send, Singleton, Super, Test, Throw, Try, Variable } from '../model'
import { ANY, AtomicType, ELEMENT, RETURN, TypeSystemProblem, VOID, WollokAtomicType, WollokClosureType, WollokMethodType, WollokModuleType, WollokParameterType, WollokParametricType, WollokType, WollokUnionType } from './wollokTypes'
Expand Down Expand Up @@ -31,7 +32,7 @@ function newTVarFor(node: Node) {
newTVar.setType(new WollokMethodType(annotatedVar, parameters, annotatedVariableMap(node)), false)
}
if (node.is(Singleton) && node.isClosure()) {
const methodApply = node.methods.find(_ => _.name === '<apply>')!
const methodApply = node.methods.find(_ => _.name === CLOSURE_METHOD_NAME)!
const parameters = methodApply.parameters.map(typeVariableFor)
// annotatedVar = newSynteticTVar() // But for methods, annotations reference to return tVar
const returnType = typeVariableFor(methodApply).atParam(RETURN)
Expand Down
36 changes: 21 additions & 15 deletions src/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
// - Level could be different for the same Expectation on different nodes
// - Problem could know how to convert to string, receiving the interpolation function (so it can be translated). This could let us avoid having parameters.
// - Good default for simple problems, but with a config object for more complex, so we know what is each parameter
import { INITIALIZE_METHOD_NAME, KEYWORDS, OBJECT_MODULE, WOLLOK_BASE_PACKAGE } from './constants'
import { CLOSURE_METHOD_NAME, INITIALIZE_METHOD_NAME, KEYWORDS, OBJECT_MODULE, WOLLOK_BASE_PACKAGE } from './constants'
import { count, duplicates, is, isEmpty, last, List, match, notEmpty, TypeDefinition, when } from './extensions'
// - Unified problem type
import { Assignment, Body, Catch, Class, Code, Describe, Entity, Expression, Field, If, Import,
Expand Down Expand Up @@ -755,20 +755,26 @@ const isGetter = (node: Method): boolean => node.parent.allFields.map(_ => _.nam

const methodOrTestUsesField = (parent: Method | Test, field: Field) => parent.sentences.some(sentence => usesField(sentence, field))

const usesField = (node: Sentence | Body | NamedArgument, field: Field): boolean => match(node)(
when(Variable)(node => usesField(node.value, field)),
when(Return)(node => !!node.value && usesField(node.value, field)),
when(Assignment)(node => node.variable.target === field || usesField(node.value, field)),
when(Reference)(node => node.target === field),
when(Send)(node => usesField(node.receiver, field) || node.args.some(arg => usesField(arg, field))),
when(If)(node => usesField(node.condition, field) || usesField(node.thenBody, field) || node.elseBody && usesField(node.elseBody, field)),
when(New)(node => node.args.some(arg => usesField(arg, field))),
when(NamedArgument)(node => usesField(node.value, field)),
when(Throw)(node => usesField(node.exception, field)),
when(Try)(node => usesField(node.body, field) || node.catches.some(catchBlock => usesField(catchBlock.body, field)) || !!node.always && usesField(node.always, field)),
when(Expression)(() => false),
when(Body)(node => node.sentences.some(sentence => usesField(sentence, field))),
)
const usesField = (node: Sentence | Body | NamedArgument, field: Field): boolean =>
match(node)(
when(Singleton)(node => {
if (!node.isClosure()) return false
const applyMethod = node.methods.find(method => method.name === CLOSURE_METHOD_NAME)
return !!applyMethod && methodOrTestUsesField(applyMethod, field)
}),
when(Variable)(node => usesField(node.value, field)),
when(Return)(node => !!node.value && usesField(node.value, field)),
when(Assignment)(node => node.variable.target === field || usesField(node.value, field)),
when(Reference)(node => node.target === field),
when(Send)(node => usesField(node.receiver, field) || node.args.some(arg => usesField(arg, field))),
when(If)(node => usesField(node.condition, field) || usesField(node.thenBody, field) || node.elseBody && usesField(node.elseBody, field)),
when(New)(node => node.args.some(arg => usesField(arg, field))),
when(NamedArgument)(node => usesField(node.value, field)),
when(Throw)(node => usesField(node.exception, field)),
when(Try)(node => usesField(node.body, field) || node.catches.some(catchBlock => usesField(catchBlock.body, field)) || !!node.always && usesField(node.always, field)),
when(Expression)(() => false),
when(Body)(node => node.sentences.some(sentence => usesField(sentence, field))),
)

// TODO: Import could offer a list of imported entities
const entityIsAlreadyUsedInImport = (target: Entity | undefined, entityName: string) => target && match(target)(
Expand Down
3 changes: 2 additions & 1 deletion src/wre/lang.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { CLOSURE_METHOD_NAME } from '../constants'
import { hash, isEmpty, List } from '../extensions'
import { Evaluation, Execution, Frame, Natives, RuntimeObject, RuntimeValue } from '../interpreter/runtimeModel'
import { Class, Node, Singleton } from '../model'
Expand Down Expand Up @@ -702,7 +703,7 @@ const lang: Natives = {
*apply(this: Evaluation, self: RuntimeObject, args: RuntimeObject): Execution<RuntimeValue> {
args.assertIsCollection()

const method = self.module.lookupMethod('<apply>', args.innerCollection.length)
const method = self.module.lookupMethod(CLOSURE_METHOD_NAME, args.innerCollection.length)
if (!method) return yield* this.send('messageNotUnderstood', self, yield* this.reify('apply'), args)

const locals = yield* this.localsFor(method, args.innerCollection)
Expand Down

0 comments on commit 2431dec

Please sign in to comment.