Skip to content

Commit

Permalink
Merge pull request #129 from uqbar-project/wollok-ts-api-revamp
Browse files Browse the repository at this point in the history
wollok-ts API revamp
  • Loading branch information
PalumboN authored Mar 20, 2024
2 parents bcf4129 + 794569b commit 25a6bda
Show file tree
Hide file tree
Showing 15 changed files with 61 additions and 116 deletions.
4 changes: 3 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
{
"files.trimTrailingWhitespace": true
"files.trimTrailingWhitespace": true,
"typescript.preferences.quoteStyle": "single",
"typescript.format.semicolons": "remove"
}
14 changes: 7 additions & 7 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
"pkg": "^5.8.1",
"socket.io": "^4.5.1",
"winston": "^3.11.0",
"wollok-ts": "4.0.9"
"wollok-ts": "4.1.0"
},
"devDependencies": {
"@types/chai": "^4.3.9",
Expand Down
3 changes: 1 addition & 2 deletions src/commands/extrasGame.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { RuntimeObject } from 'wollok-ts'
import { Interpreter } from 'wollok-ts/dist/interpreter/interpreter'
import { Interpreter, RuntimeObject } from 'wollok-ts'

const { round } = Math

Expand Down
9 changes: 5 additions & 4 deletions src/commands/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { existsSync, writeFileSync } from 'node:fs'
import { basename, join } from 'node:path'
import { userInfo } from 'os'
import { ENTER, createFolderIfNotExists } from '../utils'
import { PROGRAM_FILE_EXTENSION, TEST_FILE_EXTENSION, WOLLOK_FILE_EXTENSION } from 'wollok-ts'

export type Options = {
project: string,
Expand Down Expand Up @@ -31,17 +32,17 @@ export default function ({ project, name, noTest = false, noCI = false, game = f

// Creating files
const exampleName = name ?? 'example'
logger.info(`Creating definition file ${exampleName}.wlk`)
writeFileSync(join(project, `${exampleName}.wlk`), wlkDefinition)
logger.info(`Creating definition file ${exampleName}.${WOLLOK_FILE_EXTENSION}`)
writeFileSync(join(project, `${exampleName}.${WOLLOK_FILE_EXTENSION}`), wlkDefinition)

if (!noTest) {
const testFile = `test${capitalizeFirstLetter(exampleName)}.wtest`
const testFile = `test${capitalizeFirstLetter(exampleName)}.${TEST_FILE_EXTENSION}`
logger.info(`Creating test file ${testFile}`)
writeFileSync(join(project, testFile), testDefinition(exampleName))
}

if (game) {
const gameFile = `main${capitalizeFirstLetter(exampleName)}.wpgm`
const gameFile = `main${capitalizeFirstLetter(exampleName)}.${PROGRAM_FILE_EXTENSION}`
logger.info(`Creating program file ${gameFile}`)
writeFileSync(join(project, `${gameFile}`), gameDefinition(exampleName))
}
Expand Down
26 changes: 15 additions & 11 deletions src/commands/repl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,9 @@ import http from 'http'
import logger from 'loglevel'
import { CompleterResult, Interface, createInterface as Repl } from 'readline'
import { Server, Socket } from 'socket.io'
import { Entity, Environment, Evaluation, Import, Package, Reference, Sentence, WollokException, parse } from 'wollok-ts'
import { notEmpty } from 'wollok-ts/dist/extensions'
import { Interpreter } from 'wollok-ts/dist/interpreter/interpreter'
import link from 'wollok-ts/dist/linker'
import { ParseError } from 'wollok-ts/dist/parser'
import natives from 'wollok-ts/dist/wre/wre.natives'
import { Entity, Environment, Evaluation, Import, link, notEmpty, Package, Reference, Sentence, WollokException, parse, TO_STRING_METHOD, RuntimeObject, linkSentenceInNode, Interpreter, ParseError, WRENatives as natives } from 'wollok-ts'
import { getDataDiagram } from '../services/diagram-generator'
import { buildEnvironmentForProject, failureDescription, getFQN, linkSentence, publicPath, successDescription, valueDescription, validateEnvironment, handleError, ENTER, serverError, stackTrace, replIcon } from '../utils'
import { buildEnvironmentForProject, failureDescription, getFQN, publicPath, successDescription, valueDescription, validateEnvironment, handleError, ENTER, serverError, stackTrace, replIcon } from '../utils'
import { logger as fileLogger } from '../logger'
import { TimeMeasurer } from '../time-measurer'

Expand Down Expand Up @@ -194,14 +189,16 @@ function defineCommands(autoImportPath: string | undefined, options: Options, re
// EVALUATION
// ══════════════════════════════════════════════════════════════════════════════════════════════════════════════════

// TODO WOLLOK-TS: check if we should decouple the function
export function interprete(interpreter: Interpreter, line: string): string {
try {
const sentenceOrImport = parse.Import.or(parse.Variable).or(parse.Assignment).or(parse.Expression).tryParse(line)
const error = [sentenceOrImport, ...sentenceOrImport.descendants].flatMap(_ => _.problems ?? []).find(_ => _.level === 'error')
if (error) throw error

if (sentenceOrImport.is(Sentence)) {
linkSentence(sentenceOrImport, interpreter.evaluation.environment)
const environment = interpreter.evaluation.environment
linkSentenceInNode(sentenceOrImport, replNode(environment))
const unlinkedNode = [sentenceOrImport, ...sentenceOrImport.descendants].find(_ => _.is(Reference) && !_.target)

if (unlinkedNode) {
Expand All @@ -213,9 +210,7 @@ export function interprete(interpreter: Interpreter, line: string): string {

const result = interpreter.exec(sentenceOrImport)
const stringResult = result
? typeof result.innerValue === 'string'
? `"${result.innerValue}"`
: interpreter.send('toString', result)!.innerString!
? showInnerValue(interpreter, result)
: ''
return successDescription(stringResult)
}
Expand All @@ -242,6 +237,13 @@ export function interprete(interpreter: Interpreter, line: string): string {
}
}

function showInnerValue(interpreter: Interpreter, obj: RuntimeObject) {
if (obj!.innerValue === null) return 'null'
return typeof obj.innerValue === 'string'
? `"${obj.innerValue}"`
: interpreter.send(TO_STRING_METHOD, obj)!.innerString!
}

async function autocomplete(input: string): Promise<CompleterResult> {
const completions = ['fafafa', 'fefefe', 'fofofo']
const hits = completions.filter((c) => c.startsWith(input))
Expand Down Expand Up @@ -300,6 +302,8 @@ export async function initializeClient(options: Options, repl: Interface, interp
}
}

// TODO WOLLOK-TS: migrate it? Maybe it could be part of Environment
// Environment.newImportFor(baseNode, importNode)
function newImport(importNode: Import, environment: Environment) {
const node = replNode(environment)
const imported = node.scope.resolve<Package | Entity>(importNode.entity.name)!
Expand Down
9 changes: 3 additions & 6 deletions src/commands/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ import http from 'http'
import logger from 'loglevel'
import { join, relative } from 'path'
import { Server, Socket } from 'socket.io'
import { Environment, link, Name, Package, parse, RuntimeObject, WollokException } from 'wollok-ts'
import interpret, { Interpreter } from 'wollok-ts/dist/interpreter/interpreter'
import natives from 'wollok-ts/dist/wre/wre.natives'
import { Environment, GAME_MODULE, link, Name, Package, parse, RuntimeObject, WollokException, interpret, Interpreter, WRENatives as natives } from 'wollok-ts'
import { ENTER, buildEnvironmentForProject, buildEnvironmentIcon, failureDescription, folderIcon, gameIcon, handleError, isImageFile, programIcon, publicPath, readPackageProperties, serverError, stackTrace, successDescription, validateEnvironment, valueDescription } from '../utils'
import { buildKeyPressEvent, canvasResolution, Image, queueEvent, visualState, VisualState, wKeyCode } from './extrasGame'
import { getDataDiagram } from '../services/diagram-generator'
Expand All @@ -26,7 +24,6 @@ type Options = {
startDiagram: boolean
}

// TODO: Decouple io from getInterpreter
let timer = 0

const DEFAULT_PORT = '4200'
Expand Down Expand Up @@ -89,7 +86,7 @@ export const getGameInterpreter = (environment: Environment, io: Server): Interp
drawer: {
*apply() {
try {
const game = interpreter?.object('wollok.game.game')
const game = interpreter?.object(GAME_MODULE)
const background = game.get('boardGround') ? game.get('boardGround')?.innerString : 'default'
const visuals = getVisuals(game, interpreter)
io.emit('background', background)
Expand Down Expand Up @@ -118,7 +115,7 @@ export const getGameInterpreter = (environment: Environment, io: Server): Interp

const interpreter = interpret(environment, nativesAndDraw)

const gameSingleton = interpreter?.object('wollok.game.game')
const gameSingleton = interpreter?.object(GAME_MODULE)
const drawer = interpreter.object('draw.drawer')
interpreter.send('onTick', gameSingleton, interpreter.reify(17), interpreter.reify('renderizar'), drawer)

Expand Down
15 changes: 6 additions & 9 deletions src/commands/test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { bold } from 'chalk'
import { time, timeEnd } from 'console'
import logger from 'loglevel'
import { Entity, Environment, Node, Test } from 'wollok-ts'
import { is, match, when } from 'wollok-ts/dist/extensions'
import interpret from 'wollok-ts/dist/interpreter/interpreter'
import natives from 'wollok-ts/dist/wre/wre.natives'
import { Entity, Environment, Node, Test, is, match, when, WRENatives as natives, interpret } from 'wollok-ts'
import { buildEnvironmentForProject, failureDescription, successDescription, valueDescription, validateEnvironment, handleError, ENTER, stackTrace, buildEnvironmentIcon, testIcon } from '../utils'
import { logger as fileLogger } from '../logger'
import { TimeMeasurer } from '../time-measurer'
Expand All @@ -31,7 +28,7 @@ export function getTarget(environment: Environment, filter: string | undefined,
const fqnByOptionalParameters = [file, describe, test].filter(Boolean).join('.')
const filterTest = sanitize(filter) ?? fqnByOptionalParameters ?? ''
const possibleTargets = environment.descendants.filter(is(Test))
const onlyTarget = possibleTargets.find(test => test.isOnly)
const onlyTarget = possibleTargets.find((test: Test) => test.isOnly)
const testMatches = (filter: string) => (test: Test) => !filter || sanitize(test.fullyQualifiedName)!.includes(filter)
return onlyTarget ? [onlyTarget] : possibleTargets.filter(testMatches(filterTest))
}
Expand Down Expand Up @@ -64,8 +61,8 @@ export default async function (filter: string | undefined, options: Options): Pr
const failures: [Test, Error][] = []
let successes = 0

environment.forEach(node => match(node)(
when(Test)(node => {
environment.forEach((node: Node) => match(node)(
when(Test)((node: Test) => {
if (targets.includes(node)) {
const tabulation = tabulationForNode(node)
try {
Expand All @@ -79,14 +76,14 @@ export default async function (filter: string | undefined, options: Options): Pr
}
}),

when(Entity)(node => {
when(Entity)((node: Entity) => {
const tabulation = tabulationForNode(node)
if(targets.some(target => node.descendants.includes(target))){
logger.info(tabulation, node.name)
}
}),

when(Node)( _ => { }),
when(Node)((_: Node) => { }),
))

log()
Expand Down
1 change: 0 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import logger from 'loglevel'
import { version } from '../package.json'
import { cyan } from 'chalk'


const program = new Command()
.name('wollok')
.description('Wollok Language command line interpreter tool')
Expand Down
55 changes: 13 additions & 42 deletions src/services/diagram-generator.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,17 @@
import { ElementDefinition } from 'cytoscape'
import { Entity, Import, InnerValue, Package, RuntimeObject } from 'wollok-ts'
import { Interpreter } from 'wollok-ts/dist/interpreter/interpreter'
import { BOOLEAN_MODULE, CLOSURE_MODULE, DATE_MODULE, DICTIONARY_MODULE, Entity, InnerValue, KEYWORDS, LIST_MODULE, NUMBER_MODULE, Package, PAIR_MODULE, RANGE_MODULE, RuntimeObject, STRING_MODULE, TO_STRING_METHOD, WOLLOK_BASE_PACKAGE, Interpreter } from 'wollok-ts'
import { REPL, replNode } from '../commands/repl'
import { isConstant, isREPLConstant } from '../utils'
import { isREPLConstant } from '../utils'

type objectType = 'literal' | 'object' | 'null'

const LIST_MODULE = 'List'
const STRING_MODULE = 'wollok.lang.String'
const WOLLOK_BASE_MODULES = 'wollok.'

const SELF = 'self'

function getImportedDefinitions(interpreter: Interpreter, rootFQN?: Package): Entity[] {
const environment = interpreter.evaluation.environment
const importedPackage = rootFQN ?? replNode(environment)
return [
...importedPackage.members,
...importedPackage.imports.flatMap(resolveImport),
]
}

function resolveImport(imp: Import): Entity[] {
const importedEntity = imp.entity.target!
return imp.isGeneric
? [...(importedEntity as Package).members]
: [importedEntity]
}

export function getDataDiagram(interpreter: Interpreter, rootFQN?: Package): ElementDefinition[] {
const importedFromConsole = getImportedDefinitions(interpreter, rootFQN)
const environment = interpreter.evaluation.environment
const importedFromConsole = (replNode(environment) ?? rootFQN!).allScopedEntities()
const currentFrame = interpreter.evaluation.currentFrame
const objects = new Map(Array.from(currentFrame.locals.keys()).map((name) => [name, currentFrame.get(name)]))

// TODO: Aclarar qué está haciendo
return Array.from(objects.keys())
.filter((name) => {
const object = objects.get(name)
Expand Down Expand Up @@ -95,7 +74,6 @@ function elementFromObject(obj: RuntimeObject, interpreter: Interpreter, already
])
}


function concatOverlappedReferences(elementDefinitions: ElementDefinition[]): ElementDefinition[] {
const cleanDefinitions: ElementDefinition[] = []
elementDefinitions.forEach(elem => {
Expand All @@ -114,7 +92,6 @@ function concatOverlappedReferences(elementDefinitions: ElementDefinition[]): El
return cleanDefinitions
}


// De acá se obtiene la lista de objetos a dibujar
function decoration(obj: RuntimeObject, interpreter: Interpreter) {
const moduleName: string = obj.module.fullyQualifiedName
Expand All @@ -132,20 +109,19 @@ function isConsoleLocal(name: string): boolean {
}

function isLanguageLocal(name: string) {
return name.startsWith(WOLLOK_BASE_MODULES) || ['true', 'false', 'null'].includes(name)
return name.startsWith(WOLLOK_BASE_PACKAGE) || ['true', 'false', 'null'].includes(name)
}

function getType(obj: RuntimeObject, moduleName: string): objectType {
if (obj.innerValue === null) return 'null'
return moduleName.startsWith(WOLLOK_BASE_MODULES) ? 'literal' : 'object'
return moduleName.startsWith(WOLLOK_BASE_PACKAGE) ? 'literal' : 'object'
}

function getLabel(obj: RuntimeObject, interpreter: Interpreter): string {
const { innerValue, module } = obj
if (innerValue === null) return 'null'
const moduleName = module.fullyQualifiedName
if (shouldShortenRepresentation(moduleName)) return showInnerValue(interpreter.send('toString', obj)?.innerValue)
// Otra opción es enviar el mensaje "printString" pero por cuestiones de performance preferí aprovechar el innerValue
if (shouldShortenRepresentation(moduleName)) return showInnerValue(interpreter.send(TO_STRING_METHOD, obj)?.innerValue)
if (moduleName === STRING_MODULE) return `"${showInnerValue(innerValue)}"`
if (shouldShowInnerValue(moduleName)) return showInnerValue(innerValue)
return module.name ?? 'Object'
Expand All @@ -159,12 +135,11 @@ function getFontSize(text: string) {
}

function shouldShortenRepresentation(moduleName: string) {
// Por ahora el Closure está viniendo como `wollok.lang.Closure#undefined` supongo que porque está en el contexto de un REPL
return ['wollok.lang.Date', 'wollok.lang.Pair', 'wollok.lang.Range', 'wollok.lang.Dictionary'].includes(moduleName) || moduleName.startsWith('wollok.lang.Closure')
return [DATE_MODULE, PAIR_MODULE, RANGE_MODULE, DICTIONARY_MODULE].includes(moduleName) || moduleName.startsWith(CLOSURE_MODULE)
}

function shouldShowInnerValue(moduleName: string) {
return ['wollok.lang.String', 'wollok.lang.Number', 'wollok.lang.Boolean'].includes(moduleName)
return [STRING_MODULE, NUMBER_MODULE, BOOLEAN_MODULE].includes(moduleName)
}

function shouldIterateChildren(moduleName: string): boolean {
Expand All @@ -179,7 +154,7 @@ function getLocalKeys(obj: RuntimeObject) {
const { innerValue, module } = obj
if (innerValue === null) return []
const moduleName: string = module.fullyQualifiedName
return shouldIterateChildren(moduleName) ? [...obj.locals.keys()].filter(key => key !== SELF) : []
return shouldIterateChildren(moduleName) ? [...obj.locals.keys()].filter(key => key !== KEYWORDS.SELF) : []
}

function buildReference(obj: RuntimeObject, label: string) {
Expand All @@ -188,7 +163,7 @@ function buildReference(obj: RuntimeObject, label: string) {
return {
data: {
id: `${id}_${runtimeValue?.id}`,
label: `${label}${isConstant(obj, label) ? '🔒' : ''}`,
label: `${label}${obj.isConstant(label) ? '🔒' : ''}`,
source: id,
target: runtimeValue?.id,
style: 'solid',
Expand All @@ -207,7 +182,7 @@ function getCollections(obj: RuntimeObject, interpreter: Interpreter, alreadyVis
id: `${id}_${item.id}`,
source: id,
target: item.id,
label: isList(obj.module.name) ? i.toString() : '',
label: obj.module.fullyQualifiedName === LIST_MODULE ? i.toString() : '',
style: 'dotted',
width: 1,
},
Expand All @@ -219,10 +194,6 @@ function getCollections(obj: RuntimeObject, interpreter: Interpreter, alreadyVis
})
}

function isList(moduleName: string | undefined) {
return moduleName === LIST_MODULE
}

function getInstanceVariables(obj: RuntimeObject, interpreter: Interpreter, alreadyVisited: string[]) {
const { id } = obj
return getLocalKeys(obj).flatMap(name => [
Expand Down
Loading

0 comments on commit 25a6bda

Please sign in to comment.