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

Modified the language server so it sends the results of the interpreter #3

Merged
merged 18 commits into from
Oct 28, 2023
3 changes: 3 additions & 0 deletions langium-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
"fileExtensions": [".lox"],
"textMate": {
"out": "syntaxes/lox.tmLanguage.json"
},
"monarch": {
"out": "syntaxes/lox.monarch.ts"
}
}],
"out": "src/language-server/generated"
Expand Down
559 changes: 472 additions & 87 deletions package-lock.json

Large diffs are not rendered by default.

62 changes: 36 additions & 26 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,37 @@
"Programming Languages"
],
"contributes": {
"languages": [{
"id": "lox",
"aliases": ["Lox", "lox"],
"extensions": [".lox"],
"configuration": "./language-configuration.json"
}],
"grammars": [{
"language": "lox",
"scopeName": "source.lox",
"path": "./syntaxes/lox.tmLanguage.json"
}],
"notebooks": [{
"type": "lox-notebook",
"displayName": "Lox Notebook",
"selector": [
{
"filenamePattern": "*.loxnb"
}
]
}]
"languages": [
{
"id": "lox",
"aliases": [
"Lox",
"lox"
],
"extensions": [
".lox"
],
"configuration": "./language-configuration.json"
}
],
"grammars": [
{
"language": "lox",
"scopeName": "source.lox",
"path": "./syntaxes/lox.tmLanguage.json"
}
],
"notebooks": [
{
"type": "lox-notebook",
"displayName": "Lox Notebook",
"selector": [
{
"filenamePattern": "*.loxnb"
}
]
}
]
},
"activationEvents": [
"onLanguage:lox"
Expand All @@ -49,18 +60,16 @@
"build": "tsc -b tsconfig.json",
"watch": "tsc -b tsconfig.json --watch",
"lint": "eslint src --ext ts",
"clean": "shx rm -rf out node_modules",
msujew marked this conversation as resolved.
Show resolved Hide resolved
"langium:generate": "langium generate",
"langium:watch": "langium generate --watch"
},
"dependencies": {
"chevrotain": "^9.1.0",
"colors": "^1.4.0",
"commander": "^8.0.0",
"langium": "1.0.1",
"langium": "~1.2.1",
emilkrebs marked this conversation as resolved.
Show resolved Hide resolved
"uuid": "^9.0.0",
"vscode-languageclient": "8.0.2",
"vscode-languageserver": "8.0.2",
"vscode-uri": "^3.0.2"
"vscode-languageclient": "^8.1.0"
},
"devDependencies": {
"@types/node": "^14.17.3",
Expand All @@ -69,7 +78,8 @@
"@typescript-eslint/eslint-plugin": "^4.14.1",
"@typescript-eslint/parser": "^4.14.1",
"eslint": "^7.19.0",
"langium-cli": "1.0.0",
"langium-cli": "~1.2.1",
"shx": "^0.3.4",
"typescript": "^4.6.2"
}
}
9 changes: 6 additions & 3 deletions src/interpreter/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { BinaryExpression, Expression, isBinaryExpression, isBooleanExpression,
import { createLoxServices } from "../language-server/lox-module";
import { v4 } from 'uuid';
import { URI } from "vscode-uri";
import { CancellationToken } from "vscode-languageclient";
import { CancellationToken } from "vscode-languageserver";

export interface InterpreterContext {
log: (value: unknown) => MaybePromise<void>
Expand Down Expand Up @@ -88,7 +88,7 @@ async function buildDocument(program: string): Promise<BuildResult> {
}
}

async function runProgram(program: LoxProgram, outerContext: InterpreterContext): Promise<void> {
export async function runProgram(program: LoxProgram, outerContext: InterpreterContext): Promise<void> {
const context: RunnerContext = {
variables: new Variables(),
log: outerContext.log
Expand All @@ -99,6 +99,9 @@ async function runProgram(program: LoxProgram, outerContext: InterpreterContext)
if (!isClass(statement) && !isFunctionDeclaration(statement)) {
await runLoxElement(statement, context, () => { end = true });
}
else if(isClass(statement)){
throw new AstNodeError(statement, 'Classes are currently unsupported');
}
if (end) {
break;
}
Expand Down Expand Up @@ -255,7 +258,7 @@ async function runMemberCall(memberCall: MemberCall, context: RunnerContext): Pr
} else if (isVariableDeclaration(ref) || isParameter(ref)) {
value = context.variables.get(memberCall, ref.name);
} else if (isClass(ref)) {
throw new AstNodeError(memberCall, 'Classes are current unsupported');
throw new AstNodeError(memberCall, 'Classes are currently unsupported');
} else {
value = previous;
}
Expand Down
10 changes: 9 additions & 1 deletion src/language-server/lox-validator.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { AstNode, streamAllContents, ValidationAcceptor, ValidationChecks, ValidationRegistry } from 'langium';
import { BinaryExpression, ExpressionBlock, FunctionDeclaration, isReturnStatement, LoxAstType, MethodMember, TypeReference, UnaryExpression, VariableDeclaration } from './generated/ast';
import { BinaryExpression, Class, ExpressionBlock, FunctionDeclaration, isReturnStatement, LoxAstType, MethodMember, TypeReference, UnaryExpression, VariableDeclaration } from './generated/ast';
import type { LoxServices } from './lox-module';
import { isAssignable } from './type-system/assignment';
import { isVoidType, TypeDescription, typeToString } from './type-system/descriptions';
Expand All @@ -18,6 +18,7 @@ export class LoxValidationRegistry extends ValidationRegistry {
UnaryExpression: validator.checkUnaryOperationAllowed,
VariableDeclaration: validator.checkVariableDeclaration,
MethodMember: validator.checkMethodReturnType,
Class: validator.checkClassDeclaration,
FunctionDeclaration: validator.checkFunctionReturnType
};
this.register(checks, validator);
Expand All @@ -37,6 +38,13 @@ export class LoxValidator {
this.checkFunctionReturnTypeInternal(method.body, method.returnType, accept);
}

// TODO: implement classes
checkClassDeclaration(declaration: Class, accept: ValidationAcceptor): void {
accept('error', 'Classes are currently unsupported.', {
node: declaration
msujew marked this conversation as resolved.
Show resolved Hide resolved
});
}

private checkFunctionReturnTypeInternal(body: ExpressionBlock, returnType: TypeReference, accept: ValidationAcceptor): void {
const map = this.getTypeCache();
const returnStatements = streamAllContents(body).filter(isReturnStatement).toArray();
Expand Down
66 changes: 66 additions & 0 deletions src/language-server/main-browser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/******************************************************************************
* Copyright 2022 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/

import { startLanguageServer, EmptyFileSystem, DocumentState, LangiumDocument } from 'langium';
import { BrowserMessageReader, BrowserMessageWriter, Diagnostic, NotificationType, createConnection } from 'vscode-languageserver/browser';
import { createLoxServices } from './lox-module';
import { runInterpreter } from '../interpreter/runner';

declare const self: DedicatedWorkerGlobalScope;

/* browser specific setup code */
const messageReader = new BrowserMessageReader(self);
const messageWriter = new BrowserMessageWriter(self);

const connection = createConnection(messageReader, messageWriter);

// Inject the shared services and language-specific services
const { shared } = createLoxServices({ connection, ...EmptyFileSystem });

// Start the language server with the shared services
startLanguageServer(shared);

// Send a notification with the serialized AST after every document change
type DocumentChange = { uri: string, content: string, diagnostics: Diagnostic[] };
const documentChangeNotification = new NotificationType<DocumentChange>('browser/DocumentChange');
shared.workspace.DocumentBuilder.onBuildPhase(DocumentState.Validated, async documents => {
for (const document of documents) {
if (document.diagnostics === undefined || document.diagnostics.filter((i) => i.severity === 1).length === 0) {

sendMessage(document, "notification", "startInterpreter")
const timeoutId = setTimeout(() => {
sendMessage(document, "error", "Interpreter timed out");
}, 1000 * 60); // 1 minute

await Promise.race([
runInterpreter(document.textDocument.getText(), {
log: (message) => {
sendMessage(document, "output", message);
}
}).catch((e) => {
msujew marked this conversation as resolved.
Show resolved Hide resolved
console.error(e);
sendMessage(document, "error", e.message);
}).then(() => {
sendMessage(document, "notification", "endInterpreter");
}).finally(() => {
clearTimeout(timeoutId);
}),
new Promise(() => timeoutId)
]);
}
else {
sendMessage(document, "error", document.diagnostics)
}
}
});

function sendMessage(document: LangiumDocument, type: string, content: unknown): void {
connection.sendNotification(documentChangeNotification, {
uri: document.uri.toString(),
content: JSON.stringify({ type, content }),
diagnostics: document.diagnostics ?? []
});
}
30 changes: 30 additions & 0 deletions syntaxes/lox.monarch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Monarch syntax highlighting for the lox language.
export default {
keywords: [
'and','boolean','class','else','false','for','fun','if','nil','number','or','print','return','string','super','this','true','var','void','while'
],
operators: [
'!','!=','*','+',',','-','.','/',':',';','<','<=','=','==','=>','>','>='
],
symbols: /!|!=|\(|\)|\*|\+|,|-|\.|/|:|;|<|<=|=|==|=>|>|>=|\{|\}/,

tokenizer: {
initial: [
{ regex: /[_a-zA-Z][\w_]*/, action: { cases: { '@keywords': {"token":"keyword"}, '@default': {"token":"ID"} }} },
{ regex: /[0-9]+(\.[0-9]+)?/, action: {"token":"number"} },
{ regex: /"[^"]*"/, action: {"token":"string"} },
{ include: '@whitespace' },
{ regex: /@symbols/, action: { cases: { '@operators': {"token":"operator"}, '@default': {"token":""} }} },
],
whitespace: [
{ regex: /\s+/, action: {"token":"white"} },
{ regex: /\/\*/, action: {"token":"comment","next":"@comment"} },
{ regex: /\/\/[^\n\r]*/, action: {"token":"comment"} },
],
comment: [
{ regex: /[^\/\*]+/, action: {"token":"comment"} },
{ regex: /\*\//, action: {"token":"comment","next":"@pop"} },
{ regex: /[\/\*]/, action: {"token":"comment"} },
],
}
};
13 changes: 11 additions & 2 deletions syntaxes/lox.tmLanguage.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@
{
"name": "string.quoted.double.lox",
"begin": "\"",
"end": "\""
"end": "\"",
"patterns": [
{
"include": "#string-character-escape"
}
]
}
],
"repository": {
Expand Down Expand Up @@ -47,6 +52,10 @@
"name": "comment.line.lox"
}
]
},
"string-character-escape": {
"name": "constant.character.escape.lox",
"match": "\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4}|u\\{[0-9A-Fa-f]+\\}|[0-2][0-7]{0,2}|3[0-6][0-7]?|37[0-7]?|[4-7][0-7]?|.|$)"
}
}
}
}
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"compilerOptions": {
"target": "ES6",
"module": "commonjs",
"lib": ["ESNext"],
"lib": ["ESNext", "WebWorker"],
"sourceMap": true,
"outDir": "out",
"strict": true,
Expand Down