Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
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: 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",
"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",
"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
});
}

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) => {
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