Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #171 wollok ts api revamp #219

Merged
merged 43 commits into from
Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
b8858ff
Fix #178
fdodino Feb 26, 2024
3f2a05a
add single quotes as setting
fdodino Feb 27, 2024
2c69a88
add constants + replace file extensions
fdodino Feb 27, 2024
0048c25
add game, closure, etc. modules & apply method as constants
fdodino Feb 27, 2024
7e15c61
add constants and use them
fdodino Feb 28, 2024
774f6e5
Fix tests + rename closure to apply
fdodino Feb 28, 2024
d4ba408
Add <apply> and <toString> from closure as constants
fdodino Feb 28, 2024
35fd2a0
Fix #178
fdodino Feb 26, 2024
205e6f0
add single quotes as setting
fdodino Feb 27, 2024
dbeeea0
add constants + replace file extensions
fdodino Feb 27, 2024
a076097
add game, closure, etc. modules & apply method as constants
fdodino Feb 27, 2024
7f9120e
add constants and use them
fdodino Feb 28, 2024
fe14cf3
Fix tests + rename closure to apply
fdodino Feb 28, 2024
0ae135b
Add <apply> and <toString> from closure as constants
fdodino Feb 28, 2024
c040dc7
Merge branch 'fix-#171-wollok-ts-api-revamp' of github.com:uqbar-proj…
fdodino Feb 29, 2024
8e40793
Renaming initialize method constant & using 'and' for reserved words
fdodino Feb 29, 2024
c838dc6
Big refactor for validator: splitted into several files
fdodino Mar 1, 2024
8b7e438
wollok-ts-cli migration
fdodino Mar 1, 2024
ba87271
wollok-lsp-ide migration
fdodino Mar 3, 2024
89e2ac8
Exporting interpreter functions
fdodino Mar 4, 2024
d4f27a6
ignore log
fdodino Mar 4, 2024
76e99a9
Refactors done with Nahue
fdodino Mar 7, 2024
c215bc4
Add wre import
fdodino Mar 7, 2024
134fcfa
Add more exports
fdodino Mar 7, 2024
9711294
interpreter has cumbersome export mechanism. Back to original idea
fdodino Mar 7, 2024
c849651
Add export functions
fdodino Mar 10, 2024
500cae7
exporting interpret & interpreter as definitions (no default)
fdodino Mar 11, 2024
7c1ffc4
Adding elvis for node.metadata
fdodino Mar 12, 2024
3ac9934
deleting unused definition
fdodino Mar 13, 2024
866fd1e
removing unused function
fdodino Mar 18, 2024
78e313e
add new test runner
fdodino Mar 18, 2024
71db79d
add linker test
fdodino Mar 18, 2024
e7a600f
Add tests for package - step 1
fdodino Mar 18, 2024
51e6728
Add first tests for isConstant
fdodino Mar 18, 2024
bdb7d75
Add tests for allScopedEntities
fdodino Mar 19, 2024
a942a66
add helpers tests
fdodino Mar 20, 2024
06fca6a
allAvailableMethods + refactor utils in tests
fdodino Mar 20, 2024
5bab774
add test for parent module
fdodino Mar 20, 2024
fbd76ad
Merge remote-tracking branch 'origin/master' into fix-#171-wollok-ts-…
fdodino Mar 20, 2024
4b37382
add tests for implicit import
fdodino Mar 20, 2024
26d88ee
Refactored helper tests
fdodino Mar 20, 2024
bee4010
Second refactor
fdodino Mar 20, 2024
8d23186
Update wollok version
fdodino Mar 20, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ test/sanity
src/wre/wre.json
.history
.nyc_output
coverage
coverage
wollok.log
2 changes: 2 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@
"mochaExplorer.logpanel": true,
"mochaExplorer.nodePath": "default",
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.preferences.quoteStyle": "single",
"typescript.format.semicolons": "remove",
}
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "wollok-ts",
"version": "4.1.0",
"wollokVersion": "3.2.1",
"wollokVersion": "3.2.2",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤩

"description": "TypeScript based Wollok language implementation",
"repository": "https://github.com/uqbar-project/wollok-ts",
"license": "MIT",
Expand All @@ -19,6 +19,9 @@
"test:coverage": "nyc --reporter=lcov npm run test",
"test:unit": "mocha --parallel -r ts-node/register/transpile-only test/**/*.test.ts",
"test:examples": "npm run test:wtest -- --root language/test/examples",
"test:helpers": "mocha --parallel -r ts-node/register/transpile-only test/helpers.test.ts",
"test:linker": "mocha --parallel -r ts-node/register/transpile-only test/linker.test.ts",
"test:model": "mocha --parallel -r ts-node/register/transpile-only test/model.test.ts",
"test:sanity": "npm run test:wtest -- --root language/test/sanity",
"test:validations": "mocha --parallel -r ts-node/register/transpile-only test/validator.test.ts",
"test:typeSystem": "mocha --parallel -r ts-node/register/transpile-only test/typeSystem*.test.ts",
Expand Down
45 changes: 36 additions & 9 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,40 @@
import { Name } from './model'

export const WOLLOK_EXTRA_STACK_TRACE_HEADER = 'Derived from TypeScript stack'

// Packages
export const WOLLOK_BASE_PACKAGE = 'wollok.'

export const INITIALIZE_METHOD_NAME = 'initialize'
export const CLOSURE_METHOD_NAME = '<apply>'
// Extensions
export const TEST_FILE_EXTENSION = 'wtest'
export const PROGRAM_FILE_EXTENSION = 'wpgm'
export const WOLLOK_FILE_EXTENSION = 'wlk'

// Module constants
export const COLLECTION_MODULE = 'wollok.lang.Collection'
export const LIST_MODULE = 'wollok.lang.List'
export const SET_MODULE = 'wollok.lang.Set'
export const BOOLEAN_MODULE = 'wollok.lang.Boolean'
export const NUMBER_MODULE = 'wollok.lang.Number'
export const STRING_MODULE = 'wollok.lang.String'
export const DATE_MODULE = 'wollok.lang.Date'
export const PAIR_MODULE = 'wollok.lang.Pair'
export const RANGE_MODULE = 'wollok.lang.Range'
export const DICTIONARY_MODULE = 'wollok.lang.Dictionary'
export const OBJECT_MODULE = 'wollok.lang.Object'
export const EXCEPTION_MODULE = 'wollok.lang.Exception'
export const CLOSURE_MODULE = 'wollok.lang.Closure'

export const GAME_MODULE = 'wollok.game.game'

// Special methods
export const INITIALIZE_METHOD = 'initialize'
export const TO_STRING_METHOD = 'toString'
export const APPLY_METHOD = 'apply'

// Constants for Closures
export const CLOSURE_EVALUATE_METHOD = '<apply>'
export const CLOSURE_TO_STRING_METHOD = '<toString>'

// Operators
export const PREFIX_OPERATORS: Record<Name, Name> = {
'!': 'negate',
'-': 'invert',
Expand All @@ -27,10 +55,7 @@ export const INFIX_OPERATORS = [
['**', '%'],
]

export const LIST_MODULE= 'wollok.lang.List'
export const SET_MODULE= 'wollok.lang.Set'
export const OBJECT_MODULE= 'wollok.lang.Object'

// Keywords
export const KEYWORDS = {
IF: 'if',
ELSE: 'else',
Expand Down Expand Up @@ -63,4 +88,6 @@ export const KEYWORDS = {
PROGRAM: 'program',
PACKAGE: 'package',
ONLY: 'only',
} as const
} as const

export const WOLLOK_EXTRA_STACK_TRACE_HEADER = 'Derived from TypeScript stack'
319 changes: 319 additions & 0 deletions src/helpers.ts

Large diffs are not rendered by default.

18 changes: 15 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Environment } from './model'
import { List } from './extensions'
import { fromJSON } from './jsonUtils'
import * as parse from './parser'
import { ParseError } from './parser'
import validate from './validator'
import print from './printer/print'
import WRE from './wre/wre.json'
Expand All @@ -22,17 +23,28 @@ function buildEnvironment(files: List<FileContent>, baseEnvironment: Environment
throw new Error(`Failed to parse ${name}: ${(error as Error).message}`)
}
}), baseEnvironment)

}

export * from './constants'
export * from './extensions'
export * from './helpers'
export * from './linker'
export * from './jsonUtils'
export * from './model'
export * from './interpreter/interpreter'
export * from './interpreter/runtimeModel'
export * from './typeSystem/constraintBasedTypeSystem'
export * from './printer/exceptions'
export * from './printer/utils'

export {
WRE,
WRENatives,
buildEnvironment,
parse,
link,
parse,
ParseError,
validate,
print,
WRE,
WRENatives,
}
2 changes: 1 addition & 1 deletion src/interpreter/interpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Entity, Environment, Method, Module, Name, Node, Sentence } from '../mo
import { Evaluation, Execution, ExecutionDefinition, Natives, RuntimeObject, RuntimeValue, WollokException } from './runtimeModel'


export default (environment: Environment, natives: Natives): Interpreter => new Interpreter(Evaluation.build(environment, natives))
export const interpret = (environment: Environment, natives: Natives): Interpreter => new Interpreter(Evaluation.build(environment, natives))


// TODO: Replace this with Higher Kinded Types if TS ever implements it...
Expand Down
44 changes: 25 additions & 19 deletions src/interpreter/runtimeModel.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { CLOSURE_EVALUATE_METHOD, KEYWORDS } from './../constants'
import { v4 as uuid } from 'uuid'
import { CLOSURE_METHOD_NAME, INITIALIZE_METHOD_NAME, LIST_MODULE, SET_MODULE, WOLLOK_BASE_PACKAGE, WOLLOK_EXTRA_STACK_TRACE_HEADER } from '../constants'
import { BOOLEAN_MODULE, EXCEPTION_MODULE, INITIALIZE_METHOD, LIST_MODULE, NUMBER_MODULE, OBJECT_MODULE, SET_MODULE, STRING_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'
import { Interpreter } from './interpreter'
import { getUninitializedAttributesForInstantiation, loopInAssignment } from '../validator'
import { getUninitializedAttributesForInstantiation, loopInAssignment } from '../helpers'

const { isArray } = Array
const { keys, entries } = Object
Expand Down Expand Up @@ -141,7 +142,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 === CLOSURE_METHOD_NAME
const target = this.node.is(Method) && this.node.name === CLOSURE_EVALUATE_METHOD
? this.node.parent
: this.node
return target.sourceInfo
Expand Down Expand Up @@ -205,23 +206,23 @@ export class RuntimeObject extends Context {
}

assertIsNumber(): asserts this is BasicRuntimeObject<number> {
this.assertIs('wollok.lang.Number', this.innerNumber)
this.assertIs(NUMBER_MODULE, this.innerNumber)
}

assertIsBoolean(): asserts this is BasicRuntimeObject<boolean> {
this.assertIs('wollok.lang.Boolean', this.innerBoolean)
this.assertIs(BOOLEAN_MODULE, this.innerBoolean)
}

assertIsString(): asserts this is BasicRuntimeObject<string> {
this.assertIs('wollok.lang.String', this.innerString)
this.assertIs(STRING_MODULE, this.innerString)
}

assertIsCollection(): asserts this is BasicRuntimeObject<RuntimeObject[]> {
if (!this.innerCollection) throw new TypeError(`Malformed Runtime Object: Collection inner value should be a List<RuntimeObject> but was ${this.innerValue}`)
}

assertIsException(): asserts this is BasicRuntimeObject<Error | undefined> {
if (!this.module.inherits(this.module.environment.getNodeByFQN('wollok.lang.Exception'))) throw new TypeError(`Expected an instance of Exception but got a ${this.module.fullyQualifiedName} instead`)
if (!this.module.inherits(this.module.environment.getNodeByFQN(EXCEPTION_MODULE))) throw new TypeError(`Expected an instance of Exception but got a ${this.module.fullyQualifiedName} instead`)
if(this.innerValue && !(this.innerValue instanceof Error)) {
throw this.innerValue//new TypeError('Malformed Runtime Object: Exception inner value, if defined, should be an Error')
}
Expand All @@ -235,6 +236,11 @@ export class RuntimeObject extends Context {
if (this.module.fullyQualifiedName !== moduleFQN) throw new TypeError(`Expected an instance of ${moduleFQN} but got a ${this.module.fullyQualifiedName} instead`)
if (innerValue === undefined) throw new TypeError(`Malformed Runtime Object: invalid inner value ${this.innerValue} for ${moduleFQN} instance`)
}

isConstant(localName: string): boolean {
return this.module.lookupField(localName)?.isConstant ?? false // TODO: instead of false we should throw an error
PalumboN marked this conversation as resolved.
Show resolved Hide resolved
}

}

// ══════════════════════════════════════════════════════════════════════════════════════════════════════════════════
Expand Down Expand Up @@ -398,7 +404,7 @@ export class Evaluation {

const args = node.parameters.map(parameter => this.currentFrame.get(parameter.name)!)

return (yield* native.call(this, this.currentFrame.get('self')!, ...args)) ?? undefined
return (yield* native.call(this, this.currentFrame.get(KEYWORDS.SELF)!, ...args)) ?? undefined
} else if(node.isConcrete()) {
try {
yield* this.exec(node.body!)
Expand Down Expand Up @@ -463,7 +469,7 @@ export class Evaluation {

protected *execSelf(node: Self): Execution<RuntimeValue> {
yield node
return this.currentFrame.get('self')
return this.currentFrame.get(KEYWORDS.SELF)
}

protected *execLiteral(node: Literal<LiteralValue>): Execution<RuntimeValue> {
Expand Down Expand Up @@ -526,7 +532,7 @@ export class Evaluation {

yield node

const receiver = this.currentFrame.get('self')!
const receiver = this.currentFrame.get(KEYWORDS.SELF)!
const currentMethod = node.ancestors.find(is(Method))!
//TODO: pass just the parent (not the FQN) to lookup?
const method = receiver.module.lookupMethod(currentMethod.name, node.args.length, { lookupStartFQN: currentMethod.parent.fullyQualifiedName })
Expand Down Expand Up @@ -616,7 +622,7 @@ export class Evaluation {
if(typeof value === 'boolean'){
const existing = this.rootFrame.get(`${value}`)
if(existing) return existing
const instance = new RuntimeObject(this.environment.getNodeByFQN('wollok.lang.Boolean'), this.rootFrame, value)
const instance = new RuntimeObject(this.environment.getNodeByFQN(BOOLEAN_MODULE), this.rootFrame, value)
this.rootFrame.set(`${value}`, instance)
return instance
}
Expand All @@ -630,32 +636,32 @@ export class Evaluation {
if(existing) return existing
}

const instance = new RuntimeObject(this.environment.getNodeByFQN('wollok.lang.Number'), this.rootFrame, preciseValue)
const instance = new RuntimeObject(this.environment.getNodeByFQN(NUMBER_MODULE), this.rootFrame, preciseValue)
if(isRound) this.numberCache.set(preciseValue, new WeakRef(instance))
return instance
}

if(typeof value === 'string'){
const existing = this.stringCache.get(value)?.deref()
if(existing) return existing
const instance = new RuntimeObject(this.environment.getNodeByFQN('wollok.lang.String'), this.rootFrame, value)
const instance = new RuntimeObject(this.environment.getNodeByFQN(STRING_MODULE), this.rootFrame, value)
this.stringCache.set(value, new WeakRef(instance))
return instance
}

const existing = this.rootFrame.get('null')
const existing = this.rootFrame.get(KEYWORDS.NULL)
if(existing) return existing
const instance = new RuntimeObject(this.environment.getNodeByFQN('wollok.lang.Object'), this.rootFrame, value)
this.rootFrame.set('null', instance)
const instance = new RuntimeObject(this.environment.getNodeByFQN(OBJECT_MODULE), this.rootFrame, value)
this.rootFrame.set(KEYWORDS.NULL, instance)
return instance
}

*list(...value: RuntimeObject[]): Execution<RuntimeObject> {
return new RuntimeObject(this.environment.getNodeByFQN('wollok.lang.List'), this.rootFrame, value)
return new RuntimeObject(this.environment.getNodeByFQN(LIST_MODULE), this.rootFrame, value)
}

*set(...value: RuntimeObject[]): Execution<RuntimeObject> {
const result = new RuntimeObject(this.environment.getNodeByFQN('wollok.lang.Set'), this.rootFrame, [])
const result = new RuntimeObject(this.environment.getNodeByFQN(SET_MODULE), this.rootFrame, [])
for(const elem of value)
yield* this.send('add', result, elem)
return result
Expand Down Expand Up @@ -690,7 +696,7 @@ export class Evaluation {
instance.set(field.name, initialValue)
}

yield * this.send(INITIALIZE_METHOD_NAME, instance)
yield * this.send(INITIALIZE_METHOD, instance)

if(!instance.module.name || instance.module.is(Describe))
for (const field of instance.module.allFields)
Expand Down
32 changes: 15 additions & 17 deletions src/linker.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { v4 as uuid } from 'uuid'
import { divideOn, is, List } from './extensions'
import { BaseProblem, Entity, Environment, Field, Id, Import, Level, Module, Name, Node, Package, Parameter, ParameterizedType, Reference, Scope, SourceMap } from './model'
import { BaseProblem, Entity, Environment, Field, Id, Import, Level, Module, Name, Node, Package, Parameter, ParameterizedType, Reference, Scope, Sentence, SourceMap } from './model'
const { assign } = Object


Expand All @@ -15,10 +15,6 @@ export class LinkError implements BaseProblem {
get sourceMap(): SourceMap | undefined { return undefined }
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const fail = (code: Name) => (node: Node) =>
assign(node, { problems: [...node.problems ?? [], new LinkError(code)] })

// ══════════════════════════════════════════════════════════════════════════════════════════════════════════════════
// MERGING
// ══════════════════════════════════════════════════════════════════════════════════════════════════════════════════
Expand Down Expand Up @@ -83,19 +79,10 @@ export class LocalScope implements Scope {
localContributions(): [Name, Node][] { return [...this.contributions.entries()] }
}

export const scopeContribution = (contributor: Node): List<[Name, Node]> =>
canBeReferenced(contributor) && contributor.name ? [[contributor.name, contributor]] : []

const scopeContribution = (contributor: Node): List<[Name, Node]> => {
if (
contributor.is(Entity) ||
contributor.is(Field) ||
contributor.is(Parameter)
) return contributor.name ? [[contributor.name, contributor]] : []

return []
}


const assignScopes = (environment: Environment) => {
export const assignScopes = (environment: Environment): void => {
environment.forEach((node, parent) => {
assign(node, {
scope: new LocalScope(
Expand Down Expand Up @@ -133,6 +120,7 @@ const assignScopes = (environment: Environment) => {
})
}

export const canBeReferenced = (node: Node): node is Entity | Field | Parameter => node.is(Entity) || node.is(Field) || node.is(Parameter)

// ══════════════════════════════════════════════════════════════════════════════════════════════════════════════════
// LINKER
Expand All @@ -158,4 +146,14 @@ export default (newPackages: List<Package>, baseEnvironment?: Environment): Envi
assignScopes(environment)

return environment
}

export function linkSentenceInNode<S extends Sentence>(newSentence: S, parentNode: Node): void {
const { scope } = parentNode
scope.register(...scopeContribution(newSentence))
newSentence.reduce((parentScope: Scope, node: Node) => {
const localScope = new LocalScope(parentScope, ...scopeContribution(node))
Object.assign(node, { scope: localScope, environment: parentNode.environment })
return localScope
}, scope)
}
Loading
Loading