Files
compiler-explorer/lib/parsers/asm-parser-orca.ts
Stephen Heumann 5a72fa0659 Add support for ORCA/C (#7368)
[ORCA/C](https://github.com/byteworksinc/ORCA-C) is a C compiler that
natively runs on and targets the Apple IIGS. ORCA/C and the programs it
generates can be run on modern Linux systems by using an emulation tool
called [Golden Gate](https://goldengate.gitlab.io).

This adds support for ORCA/C (run via Golden Gate) in Compiler Explorer.
It uses its own assembly format and objdumper (provided by Golden Gate),
so support for those is added. An extra configuration option was also
added to support execution via an emulator, bypassing the usual check
that executables can run natively. Asm docs for the WDC 65C816 processor
were also added.

ORCA/C and Golden Gate are not Free Software, but their rights holders
have given permission for them to be used on Compiler Explorer. Tarballs
for them can be supplied privately. See infra PR:
compiler-explorer/infra#1521

---------

Co-authored-by: Matt Godbolt <matt@godbolt.org>
2025-02-18 10:28:10 -06:00

150 lines
5.5 KiB
TypeScript

// Copyright (c) 2025, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import {AsmResultLabel, ParsedAsmResultLine} from '../../types/asmresult/asmresult.interfaces.js';
import {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces.js';
import {PropertyGetter} from '../properties.interfaces.js';
import {AsmParser} from './asm-parser.js';
export class ORCAAsmParser extends AsmParser {
labelDefinitionRe: RegExp;
operandRe: RegExp;
labelInOperandRe: RegExp;
directiveRe: RegExp;
linePreCommentRe: RegExp;
labelOpcodeOperandRe: RegExp;
buggyLabelRe: RegExp;
constructor(compilerProps: PropertyGetter) {
super(compilerProps);
this.commentRe = /^[!*;]\s.*/;
this.labelDefinitionRe = /^([A-Z_a-z~][\w~]*)/;
this.operandRe = /^((?:[A-Z_a-z~][\w~]*)?\s+[A-Za-z]{3}\s+)([^;]+)/;
this.labelInOperandRe = /^[#(>|]*([A-Z_a-z~][\w~]*)/;
this.directiveRe =
/^\s+(?:align|anop|case|codechk|datachk|dynchk|eject|err|expand|ieee|instime|keep|kind|list|longa|longi|mem|merr|msb|numsex|objcase|printer|setcom|symbol|title|65c02|65816)\s+/;
this.linePreCommentRe = /^(.{80}[^;]*);/;
this.labelOpcodeOperandRe = /^((?:[A-Z_a-z~][\w~]*)?)\s+([\w~]+)\s*(.*)/;
this.buggyLabelRe = /^([\w~]{20,})(start|entry)$/;
}
extractLabels(line: string): AsmResultLabel[] | undefined {
let match = line.match(this.operandRe);
if (match) {
const preOperandLength = match[1].length;
const operand = match[2];
if (operand === 'a' || operand === 'A') {
return undefined;
}
match = operand.match(this.labelInOperandRe);
if (match) {
const labelsInLine: AsmResultLabel[] = [];
const label = match[1];
const startCol = preOperandLength + operand.indexOf(label) + 1;
labelsInLine.push({
name: label,
range: {
startCol: startCol,
endCol: startCol + label.length,
},
});
return labelsInLine;
}
}
return undefined;
}
override processBinaryAsm(asm: string, filters: ParseFiltersAndOutputOptions) {
const result: ParsedAsmResultLine[] = [];
const asmLines = asm.split('\n');
const labelDefinitions: Record<string, number> = {};
for (let line of asmLines) {
let match = line.match(this.commentRe);
if (match) {
if (!filters.commentOnly) {
result.push({
text: line,
});
}
continue;
}
// Insert missing spaces after long labels.
// These may be omitted due to a bug in Golden Gate's dumpobj.
match = line.match(this.buggyLabelRe);
if (match) {
line = match[1] + ' ' + match[2];
}
match = line.match(this.labelDefinitionRe);
if (match) {
labelDefinitions[match[1]] = result.length + 1;
}
if (filters.commentOnly || filters.trim) {
match = line.match(this.linePreCommentRe);
if (match) {
line = match[1];
}
}
if (filters.directives) {
match = line.toLowerCase().match(this.directiveRe);
if (match) continue;
}
if (filters.trim) {
match = line.match(this.labelOpcodeOperandRe);
if (match) {
line = match[1] + (match[1].length === 0 ? ' ' : ' ') + match[2] + ' ' + match[3];
}
}
if (result.length === 0 || (filters.trim && result[result.length - 1].text.trim() !== 'end')) {
if (line.trim() === '') continue;
}
result.push({
text: line.trimEnd(),
labels: this.extractLabels(line),
});
}
return {
asm: result,
labelDefinitions: labelDefinitions,
};
}
}