Skip to content

Commit

Permalink
19920: Support Amalgam versions with 1-based line numbers, MINOR (#12)
Browse files Browse the repository at this point in the history
- Detect version of Amalgam executable and configure line numbers to be
one or zero based.
- Corrects issue where private or contained entity scoped label values
were not be resolved on mouse over.
- Show the ! and ^ symbols for private and contained entity label names
in the outline.
- Changes default amalgam executable to use -st postfix.
- Updates snippets.

---------

Co-authored-by: howsoRes <144272317+howsoRes@users.noreply.github.com>
  • Loading branch information
fulpm and howsoRes authored Apr 17, 2024
1 parent 7980518 commit df455c6
Show file tree
Hide file tree
Showing 12 changed files with 125 additions and 60 deletions.
29 changes: 12 additions & 17 deletions package-lock.json

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

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "amalgam-lang",
"version": "1.1.0",
"version": "1.2.0",
"type": "commonjs",
"publisher": "howso",
"displayName": "Amalgam Language",
Expand Down Expand Up @@ -38,7 +38,8 @@
"dependencies": {
"@vscode/debugadapter": "^1.59.0",
"@vscode/debugprotocol": "^1.59.0",
"async": "^3.2.4"
"async": "^3.2.4",
"semver": "^7.6.0"
},
"devDependencies": {
"@types/async": "^3.2.18",
Expand Down
43 changes: 25 additions & 18 deletions snippets/amalgam.snippets.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/debugger/activate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export function activateDebug(context: vscode.ExtensionContext, factory: vscode.
document: vscode.TextDocument,
position: vscode.Position
): vscode.ProviderResult<vscode.EvaluatableExpression> {
const VARIABLE_REGEXP = /(?<![(#"'a-z0-9_.])(?<name>[_a-z]+[_a-z0-9]*)(?!["'a-z0-9])/gi;
const VARIABLE_REGEXP = /(?<![(#"'a-z0-9_.])(?<name>[_!^a-z]+[_a-z0-9]*)(?!["'a-z0-9])/gi;
const line = document.lineAt(position.line).text;

let matches: RegExpExecArray | null;
Expand Down
2 changes: 1 addition & 1 deletion src/debugger/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export class ExternalDebugAdapterFactory implements vscode.DebugAdapterDescripto
): vscode.ProviderResult<vscode.DebugAdapterDescriptor> {
// If the executable specified in the package.json does not exist, use the system default
if (!executable) {
const command = "~/.amalgam/bin/amalgam";
const command = AmalgamDebugSession.getExecutablePath();
const args = ["--debug", "--debug-sources"];
const options = {
cwd: session.workspaceFolder?.uri?.toString(), // working directory for executable
Expand Down
4 changes: 2 additions & 2 deletions src/debugger/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { platform } from "os";
import { queue, QueueObject } from "async";
import { logger } from "../logging";
import { InvalidRuntimeResponse, RuntimeCommandCancelled, RuntimeNotStarted } from "./errors";
import { NotifySubject } from "./utils/notify";
import { NotifySubject } from "./utils";

export type DataSource = "stdout" | "stderr" | "stdin";

Expand Down Expand Up @@ -228,7 +228,7 @@ export class AmalgamRuntime extends EventEmitter {
this.commandQueue.pause();
}

public static getExecutableName(postfix: RuntimePostfix = ""): string {
public static getExecutableName(postfix: RuntimePostfix = "-st"): string {
let name: string;
switch (platform()) {
case "win32":
Expand Down
56 changes: 41 additions & 15 deletions src/debugger/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ import { DebugProtocol } from "@vscode/debugprotocol";
import { platform } from "os";
import * as path from "path";
import { existsSync as fileExists } from "fs";
import { expandUserHome } from "./utils";
import { NotifySubject } from "./utils/notify";
import * as semver from "semver";
import { expandUserHome, NotifySubject, executeCommand } from "./utils";
import { AmalgamRuntime, IRuntimeBreakpoint, IRuntimeLaunchOptions, RuntimeEvent, RuntimeVariable } from "./runtime";
import { logger as outputLogger } from "../logging";

/**
* This interface describes the Amalgam specific launch attributes (which are not part of the Debug Adapter Protocol).
Expand Down Expand Up @@ -59,9 +60,9 @@ export class AmalgamDebugSession extends LoggingDebugSession {
super();
this.parentSession = session;

// this debugger uses zero-based lines and columns
this.setDebuggerLinesStartAt1(false);
this.setDebuggerColumnsStartAt1(false);
// The Amalgam debugger uses one-based lines and columns starting with version 50.0.2
this.setDebuggerLinesStartAt1(true);
this.setDebuggerColumnsStartAt1(true);

this.runtime = new AmalgamRuntime();

Expand Down Expand Up @@ -435,11 +436,6 @@ export class AmalgamDebugSession extends LoggingDebugSession {
response.body.supportsConditionalBreakpoints = false;

this.sendResponse(response);

// Since this debug adapter can accept configuration requests like 'setBreakpoint' at any time,
// we request them early by sending an 'initializeRequest' to the frontend.
// The frontend will end the configuration sequence by calling 'configurationDone' request.
this.sendEvent(new InitializedEvent());
}

protected completionsRequest(response: DebugProtocol.CompletionsResponse): void {
Expand All @@ -460,7 +456,7 @@ export class AmalgamDebugSession extends LoggingDebugSession {
logger.setup(args.logging ? Logger.LogLevel.Verbose : Logger.LogLevel.Stop, false);

// Validate executable
const executable = this.getExecutablePath(args.executable);
const executable = AmalgamDebugSession.getExecutablePath(args.executable);
if (executable == null || !fileExists(executable)) {
this.sendErrorResponse(response, {
id: 1,
Expand Down Expand Up @@ -503,10 +499,35 @@ export class AmalgamDebugSession extends LoggingDebugSession {

this.workingDirectory = cwd || workspaceUri?.fsPath;

// wait 1 second until configuration has finished (and configurationDone has been called)
await this.configurationDone.wait(1000);
// Update debugger configuration based binary version
try {
const executableVersion = await this.getExecutableVersion(executable);
outputLogger.info(`Amalgam Version: ${executableVersion}`);
if (semver.lt(executableVersion, "50.0.2")) {
this.setDebuggerLinesStartAt1(false);
this.setDebuggerColumnsStartAt1(false);
outputLogger.debug("Line numbers set to 0-based");
}
} catch (error) {
this.sendErrorResponse(response, {
id: 3,
format: "Error: Failed to verify Amalgam binary version.",
showUser: true,
sendTelemetry: false,
});
return;
}

// Tell the front end to initialize. Normally this could be done during initializeRequest but
// we must do this after interrogating the executable version to apply version based configurations.
// The frontend will end the configuration sequence by calling 'configurationDone' request.
this.sendEvent(new InitializedEvent());

// wait up to 5 seconds until configuration has finished (and configurationDone has been called)
await this.configurationDone.wait(5000);

if (args.tracefile) {
// if a tracefile is defined, add it to the runtime arguments
const tracefile = cwd ? path.resolve(cwd, args.tracefile) : args.tracefile;
runtimeArgs.push("--tracefile", `"${tracefile}"`);
}
Expand All @@ -526,7 +547,7 @@ export class AmalgamDebugSession extends LoggingDebugSession {
} else {
this.sendErrorResponse(response, {
id: 1001,
format: "Error: Failed to start Amalgam runtime",
format: "Error: Failed to start Amalgam runtime.",
showUser: true,
sendTelemetry: false,
});
Expand Down Expand Up @@ -561,7 +582,7 @@ export class AmalgamDebugSession extends LoggingDebugSession {
* @param filePath Custom specified path.
* @returns The resolved path to Amalgam executable.
*/
private getExecutablePath(filePath?: string): string {
public static getExecutablePath(filePath?: string): string {
if (filePath == null) {
// Use default path
const executableName = AmalgamRuntime.getExecutableName();
Expand All @@ -573,6 +594,11 @@ export class AmalgamDebugSession extends LoggingDebugSession {
return filePath;
}

protected async getExecutableVersion(executable: string): Promise<semver.SemVer> {
const result = await executeCommand(executable, "--version");
return new semver.SemVer(result, { loose: true });
}

private createSource(filePath: string): Source {
if (this.workingDirectory) {
filePath = path.resolve(this.workingDirectory, filePath);
Expand Down
23 changes: 23 additions & 0 deletions src/debugger/utils/execute.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { exec } from "child_process";

/**
* Call Amalgam binary to run a one off command.
* @param executable The path to the Amalgam executable.
* @param args The command line arguments.
* @returns The stdout result.
*/
export const executeCommand = (executable: string, args: string): Promise<string> => {
return new Promise((resolve, reject) => {
exec(`"${executable}" ${args}`, (error, stdout, stderr) => {
if (error) {
reject(`error: ${error.message}`);
return;
}
if (stderr) {
reject(`stderr: ${stderr}`);
return;
}
resolve(stdout);
});
});
};
2 changes: 2 additions & 0 deletions src/debugger/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { homedir } from "os";
export * from "./execute";
export * from "./notify";

export function expandUserHome(filePath: string): string {
if (!filePath || typeof filePath !== "string") {
Expand Down
4 changes: 2 additions & 2 deletions src/language/symbols.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ export class AmalgamDocumentSymbolProvider implements vscode.DocumentSymbolProvi
} else {
// Match label
// 1: Leading text up to and including #
// 2: The opcode
// 2: The opcode (including ! or ^)
// 3: Tailing text to end of line
const reg = /^(\s*#)[\^!]?(\w+)\s*(.*)?\s*$/;
const reg = /^(\s*#)([\^!]?\w+)\s*(.*)?\s*$/;
const matches = reg.exec(line.text);

if (matches) {
Expand Down
4 changes: 2 additions & 2 deletions syntaxes/amalgam.tmLanguage.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,11 @@
"name": "variable.other.global.amalgam"
},
{
"match": "(?<=\\()(abs|accum|accum_entity_roots|accum_to_entities|acos|acosh|and|append|apply|args|asin|asinh|assign|assign_entity_roots|assign_to_entities|assoc|atan|atanh|call|call_container|call_entity|call_entity_get_changes|call_sandboxed|ceil|round|clone_entities|commonality|commonality_entities|compute_on_contained_entities|weighted_rand|compute_entity_group_kl_divergence|compute_entity_distance_contributions|compute_entity_kl_divergences|compute_entity_convictions|concat|conclude|contained_entities|contains_entity|contains_index|contains_label|contains_value|cos|cosh|create_entities|declare|dot_product|deep_set_entity_rand_seed|remove|keep|destroy_entities|difference|difference_entities|direct_assign_to_entities|direct_retrieve_from_entity|edit_distance|edit_distance_entities|entropy|erf|exp|explode|first|flatten_entity|floor|format|generalized_distance|get|get_all_labels|get_comments|get_concurrency|get_defaults|get_entity_comments|get_entity_rand_seed|get_entity_root_permission|get_labels|get_rand_seed|get_type|get_type_string|get_value|if|indices|intersect|intersect_entities|last|let|lgamma|load|load_entity|load_persistent_entity|log|max|min|mix|mix_entities|mix_labels|mod|move_entities|mutate|mutate_entity|not|or|parallel|parse|pow|crypto_sign|crypto_sign_verify|encrypt|decrypt|print|query_among|query_not_among|query_between|query_count|query_equals|query_exists|query_greater_or_equal_to|query_in_entity_list|query_min_difference|query_max_difference|query_value_masses|query_mode|query_median|query_sum|query_less_or_equal_to|query_max|query_min|query_nearest_distance|query_nearest_generalized_distance|query_not_between|query_not_equals|query_not_exists|query_not_in_entity_list|query_sample|query_weighted_sample|query_select|query_within_distance|query_within_generalized_distance|query_generalized_mean|rand|range|retrieve|retrieve_entity_root|retrieve_from_entity|reverse|seq|set|set_comments|set_concurrency|set_entity_rand_seed|set_entity_root_permission|set_labels|set_rand_seed|set_type|set_value|sin|sinh|size|sqrt|split|stack|store|store_entity|substr|system|system_time|tail|tan|tanh|tgamma|total_entity_size|total_size|trunc|union|union_entities|unparse|unzip|values|associate|while|xor|zip_labels|filter|list|map|reduce|replace|rewrite|sort|weave|zip|lambda|null|\\+|\\-|\\*|\\/|=|\\<=|\\>=|\\!=|~|\\!~| \\S+)(?=\\s+)",
"match": "(?<=\\()(abs|accum|accum_entity_roots|accum_to_entities|acos|acosh|and|append|apply|args|asin|asinh|assign|assign_entity_roots|assign_to_entities|assoc|atan|atanh|call|call_container|call_entity|call_entity_get_changes|call_sandboxed|ceil|round|clone_entities|commonality|commonality_entities|compute_on_contained_entities|weighted_rand|compute_entity_group_kl_divergence|compute_entity_distance_contributions|compute_entity_kl_divergences|compute_entity_convictions|concat|conclude|contained_entities|contains_entity|contains_index|contains_label|contains_value|cos|cosh|create_entities|declare|dot_product|deep_set_entity_rand_seed|remove|keep|destroy_entities|difference|difference_entities|direct_assign_to_entities|direct_retrieve_from_entity|edit_distance|edit_distance_entities|entropy|erf|exp|explode|first|flatten_entity|floor|format|generalized_distance|get|get_all_labels|get_comments|get_concurrency|get_defaults|get_entity_comments|get_entity_rand_seed|get_entity_root_permission|get_labels|get_rand_seed|get_type|get_type_string|get_value|if|indices|intersect|intersect_entities|last|let|lgamma|load|load_entity|load_persistent_entity|log|max|min|mix|mix_entities|mix_labels|mod|move_entities|mutate|mutate_entity|not|or|parallel|parse|pow|crypto_sign|crypto_sign_verify|encrypt|decrypt|print|query_among|query_not_among|query_between|query_count|query_equals|query_exists|query_greater_or_equal_to|query_in_entity_list|query_min_difference|query_max_difference|query_value_masses|query_mode|query_median|query_sum|query_less_or_equal_to|query_max|query_min|query_nearest_distance|query_nearest_generalized_distance|query_not_between|query_not_equals|query_not_exists|query_not_in_entity_list|query_sample|query_weighted_sample|query_select|query_within_distance|query_within_generalized_distance|query_generalized_mean|rand|range|retrieve|retrieve_entity_root|retrieve_from_entity|reverse|seq|set|set_comments|set_concurrency|set_entity_rand_seed|set_entity_root_permission|set_labels|set_rand_seed|set_type|set_value|sin|sinh|size|sqrt|split|stack|store|store_entity|substr|system|system_time|tail|tan|tanh|tgamma|total_entity_size|total_size|trunc|union|union_entities|unparse|unzip|values|associate|while|xor|zip_labels|filter|list|map|reduce|replace|rewrite|sort|weave|zip|lambda|null|target|\\+|\\-|\\*|\\/|=|\\<=|\\>=|\\!=|~|\\!~| \\S+)(?=\\s+)",
"name": "keyword.control.amalgam"
},
{
"match": "(\\(assoc\\)|\\(list\\)|\\(conclude\\)|\\(system_time\\)|\\(target\\)|\\(stack\\)|\\(args\\)|\\(contained_entities\\))",
"match": "(\\(assoc\\)|\\(list\\)|\\(conclude\\)|\\(system_time\\)|\\(target\\)|\\(stack\\)|\\(opcode_stack\\)|\\(args\\)|\\(contained_entities\\)|\\(retrieve_entity_root\\))",
"name": "keyword.control.amalgam"
},
{
Expand Down
11 changes: 11 additions & 0 deletions syntaxes/trace.tmLanguage.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,17 @@
}
}
},
{
"match": "^(DESTROY_ENTITY) (\\S+)?$",
"captures": {
"1": {
"name": "keyword.control.command.trace"
},
"2": {
"name": "support.constant.command.trace"
}
}
},
{
"match": "^([A-Z_]+)(?: (\\S+) .*)?$",
"captures": {
Expand Down

0 comments on commit df455c6

Please sign in to comment.