Skip to content

Commit 489b7e4

Browse files
benmurphyymartin-henzRichDom2185
authored
Cslang (#2831)
* Get cslang working * Fix output bug * Enable basic autocompletion for C editor * Update C compilation REPL output * Add compilation error and warning handling * Edit c compilation error messages * Migrate to Redux Toolkit part 3 (#2815) * Migrate commons actions to RTK * Migrate stories actions to RTK * Migrate side content actions to RTK * Migrate playground actions fully to RTK * Migrate GitHub actions to RTK * Migrate remote execution actions to RTK * Migrate dashboard actions to RTK * Migrate session actions to RTK * Update tests * Fix missed test * Fix incorrect test * Add hotfix for unsuccessful evaluation * Revert "Add hotfix for unsuccessful evaluation" This reverts commit 421cc9d. * Adjust warning message display for cslang * Finish integrating c-slang * Reformat code * Remove TODO * Bump c-slang * Bump c-slang * Fix lockfile issue * Fix import error * Add ignore module path to cslang * Add ignore module path to cslang * Adjust max file cache size --------- Co-authored-by: Martin Henz <henz@comp.nus.edu.sg> Co-authored-by: Richard Dominick <34370238+RichDom2185@users.noreply.github.com>
1 parent 20e0573 commit 489b7e4

File tree

9 files changed

+215
-11
lines changed

9 files changed

+215
-11
lines changed

craco.config.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const cracoConfig = (module.exports = {
1818
plugin => plugin.constructor.name === 'InjectManifest'
1919
);
2020
if (injectManifestPlugin) {
21-
injectManifestPlugin.config.maximumFileSizeToCacheInBytes = 15 * 1024 * 1024;
21+
injectManifestPlugin.config.maximumFileSizeToCacheInBytes = 17 * 1024 * 1024;
2222
}
2323

2424
// add rules to pack WASM (for Sourceror)
@@ -131,7 +131,8 @@ const cracoConfig = (module.exports = {
131131
'query-string',
132132
'decode-uri-component',
133133
'split-on-first',
134-
'filter-obj'
134+
'filter-obj',
135+
'@sourceacademy/c-slang',
135136
),
136137
'^.+\\.module\\.(css|sass|scss)$'
137138
];

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"@octokit/rest": "^20.0.0",
3535
"@reduxjs/toolkit": "^1.9.7",
3636
"@sentry/browser": "^7.57.0",
37+
"@sourceacademy/c-slang": "^1.0.18",
3738
"@sourceacademy/sharedb-ace": "^2.0.2",
3839
"@sourceacademy/sling-client": "^0.1.0",
3940
"@szhsin/react-menu": "^4.0.0",

src/commons/application/ApplicationTypes.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,13 +121,15 @@ export enum StoriesRole {
121121
export enum SupportedLanguage {
122122
JAVASCRIPT = 'JavaScript',
123123
SCHEME = 'Scheme',
124-
PYTHON = 'Python'
124+
PYTHON = 'Python',
125+
C = 'C'
125126
}
126127

127128
export const SUPPORTED_LANGUAGES = [
128129
SupportedLanguage.JAVASCRIPT,
129130
SupportedLanguage.SCHEME,
130-
SupportedLanguage.PYTHON
131+
SupportedLanguage.PYTHON,
132+
SupportedLanguage.C
131133
];
132134

133135
/**
@@ -209,6 +211,16 @@ export const pyLanguages: SALanguage[] = pySubLanguages.map(sublang => {
209211
return { ...sublang, mainLanguage: SupportedLanguage.PYTHON, supports: { repl: true } };
210212
});
211213

214+
export const cLanguages: SALanguage[] = [
215+
{
216+
chapter: Chapter.FULL_C,
217+
variant: Variant.DEFAULT,
218+
displayName: 'C',
219+
mainLanguage: SupportedLanguage.C,
220+
supports: {}
221+
}
222+
];
223+
212224
export const styliseSublanguage = (chapter: Chapter, variant: Variant = Variant.DEFAULT) => {
213225
return getLanguageConfig(chapter, variant).displayName;
214226
};
@@ -277,7 +289,8 @@ export const ALL_LANGUAGES: readonly SALanguage[] = [
277289
fullTSLanguage,
278290
htmlLanguage,
279291
...schemeLanguages,
280-
...pyLanguages
292+
...pyLanguages,
293+
...cLanguages
281294
];
282295
// TODO: Remove this function once logic has been fully migrated
283296
export const getLanguageConfig = (

src/commons/editor/Editor.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -435,10 +435,16 @@ const EditorBase = React.memo((props: EditorProps & LocalStateProps) => {
435435
session.on('changeAnnotation' as any, makeHandleAnnotationChange(session));
436436

437437
// Start autocompletion
438-
acequire('ace/ext/language_tools').setCompleters([
439-
makeCompleter((...args) => handlePromptAutocompleteRef.current(...args))
440-
]);
441-
}, [editor, props.editorTabIndex]);
438+
if (props.sourceChapter === Chapter.FULL_C) {
439+
// for C language, use the default autocomplete provided by ace editor
440+
const { textCompleter, keyWordCompleter, setCompleters } = acequire('ace/ext/language_tools');
441+
setCompleters([textCompleter, keyWordCompleter]);
442+
} else {
443+
acequire('ace/ext/language_tools').setCompleters([
444+
makeCompleter((...args) => handlePromptAutocompleteRef.current(...args))
445+
]);
446+
}
447+
}, [editor, props.sourceChapter, props.editorTabIndex]);
442448

443449
React.useLayoutEffect(() => {
444450
if (editor === undefined) {

src/commons/navigationBar/subcomponents/NavigationBarLangSelectButton.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Position } from '@blueprintjs/core';
22
import { useState } from 'react';
33
import { useDispatch } from 'react-redux';
44
import {
5+
cLanguages,
56
getLanguageConfig,
67
pyLanguages,
78
SALanguage,
@@ -21,7 +22,8 @@ const defaultSublanguages: {
2122
} = {
2223
[SupportedLanguage.JAVASCRIPT]: sourceLanguages[0],
2324
[SupportedLanguage.PYTHON]: pyLanguages[0],
24-
[SupportedLanguage.SCHEME]: schemeLanguages[0]
25+
[SupportedLanguage.SCHEME]: schemeLanguages[0],
26+
[SupportedLanguage.C]: cLanguages[0]
2527
};
2628

2729
const NavigationBarLangSelectButton = () => {

src/commons/sagas/WorkspaceSaga/helpers/evalCode.ts

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1+
import { compileAndRun as compileAndRunCCode } from '@sourceacademy/c-slang/ctowasm/dist/index';
12
import { tokenizer } from 'acorn';
23
import { Context, interrupt, Result, resume, runFilesInContext } from 'js-slang';
34
import { ACORN_PARSE_OPTIONS, TRY_AGAIN } from 'js-slang/dist/constants';
45
import { InterruptedError } from 'js-slang/dist/errors/errors';
56
import { manualToggleDebugger } from 'js-slang/dist/stdlib/inspector';
6-
import { Chapter, Variant } from 'js-slang/dist/types';
7+
import { Chapter, ErrorSeverity, ErrorType, Variant } from 'js-slang/dist/types';
78
import { SagaIterator } from 'redux-saga';
89
import { call, put, race, select, take } from 'redux-saga/effects';
910
import * as Sourceror from 'sourceror';
11+
import { makeCCompilerConfig, specialCReturnObject } from 'src/commons/utils/CToWasmHelper';
1012
import { notifyStoriesEvaluated } from 'src/features/stories/StoriesActions';
1113
import { EVAL_STORY } from 'src/features/stories/StoriesTypes';
1214

@@ -157,9 +159,91 @@ export function* evalCode(
157159
);
158160
}
159161

162+
function reportCCompilationError(errorMessage: string, context: Context) {
163+
context.errors.push({
164+
type: ErrorType.SYNTAX,
165+
severity: ErrorSeverity.ERROR,
166+
location: {
167+
start: {
168+
line: 0,
169+
column: 0
170+
},
171+
end: {
172+
line: 0,
173+
column: 0
174+
}
175+
},
176+
explain: () => errorMessage,
177+
elaborate: () => ''
178+
});
179+
}
180+
181+
function reportCRuntimeError(errorMessage: string, context: Context) {
182+
context.errors.push({
183+
type: ErrorType.RUNTIME,
184+
severity: ErrorSeverity.ERROR,
185+
location: {
186+
start: {
187+
line: 0,
188+
column: 0
189+
},
190+
end: {
191+
line: 0,
192+
column: 0
193+
}
194+
},
195+
explain: () => errorMessage,
196+
elaborate: () => ''
197+
});
198+
}
199+
200+
async function cCompileAndRun(cCode: string, context: Context) {
201+
const cCompilerConfig = await makeCCompilerConfig(cCode, context);
202+
return await compileAndRunCCode(cCode, cCompilerConfig)
203+
.then(compilationResult => {
204+
if (compilationResult.status === 'failure') {
205+
// report any compilation failure
206+
reportCCompilationError(
207+
`Compilation failed with the following error(s):\n\n${compilationResult.errorMessage}`,
208+
context
209+
);
210+
return {
211+
status: 'error',
212+
context
213+
};
214+
}
215+
if (compilationResult.warnings.length > 0) {
216+
return {
217+
status: 'finished',
218+
context,
219+
value: {
220+
toReplString: () =>
221+
`Compilation and program execution successful with the following warning(s):\n${compilationResult.warnings.join(
222+
'\n'
223+
)}`
224+
}
225+
};
226+
}
227+
if (specialCReturnObject === null) {
228+
return {
229+
status: 'finished',
230+
context,
231+
value: { toReplString: () => 'Compilation and program execution successful.' }
232+
};
233+
}
234+
return { status: 'finished', context, value: specialCReturnObject };
235+
})
236+
.catch((e: any): Result => {
237+
console.log(e);
238+
reportCRuntimeError(e.message, context);
239+
return { status: 'error' };
240+
});
241+
}
242+
160243
const isNonDet: boolean = context.variant === Variant.NON_DET;
161244
const isLazy: boolean = context.variant === Variant.LAZY;
162245
const isWasm: boolean = context.variant === Variant.WASM;
246+
const isC: boolean = context.chapter === Chapter.FULL_C;
163247

164248
let lastDebuggerResult = yield select(
165249
(state: OverallState) => state.workspaces[workspaceLocation].lastDebuggerResult
@@ -177,6 +261,8 @@ export function* evalCode(
177261
? call(resume, lastDebuggerResult)
178262
: isNonDet || isLazy || isWasm
179263
? call_variant(context.variant)
264+
: isC
265+
? call(cCompileAndRun, entrypointCode, context)
180266
: call(
181267
runFilesInContext,
182268
isFolderModeEnabled

src/commons/utils/AceHelper.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ export const getModeString = (chapter: Chapter, variant: Variant, library: strin
5050
case Chapter.SCHEME_4:
5151
case Chapter.FULL_SCHEME:
5252
return 'scheme';
53+
case Chapter.FULL_C:
54+
return 'c_cpp';
5355
default:
5456
return `source${chapter}${variant}${library}`;
5557
}

src/commons/utils/CToWasmHelper.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { ModulesGlobalConfig as CCompilerConfig } from '@sourceacademy/c-slang/ctowasm/dist';
2+
import { initModuleContext, loadModuleBundle } from 'js-slang/dist/modules/loader/moduleLoader';
3+
import { ModuleFunctions } from 'js-slang/dist/modules/moduleTypes';
4+
import { Context } from 'js-slang/dist/types';
5+
6+
import { handleConsoleLog } from '../application/actions/InterpreterActions';
7+
8+
export async function makeCCompilerConfig(
9+
program: string,
10+
context: Context
11+
): Promise<CCompilerConfig> {
12+
const externalFunctions = await loadModulesUsedInCProgram(program, context);
13+
return {
14+
printFunction: (v: string) => {
15+
if (typeof (window as any).__REDUX_STORE__ !== 'undefined') {
16+
(window as any).__REDUX_STORE__.dispatch(handleConsoleLog(context.externalContext, v));
17+
}
18+
},
19+
externalFunctions
20+
};
21+
}
22+
23+
const modulesAvailableForC = new Set(['pix_n_flix']);
24+
25+
export let specialCReturnObject: any = null;
26+
27+
/**
28+
* Load all the modules used in C Program
29+
*/
30+
export async function loadModulesUsedInCProgram(
31+
program: string,
32+
context: Context
33+
): Promise<ModuleFunctions> {
34+
const allModuleFunctions: ModuleFunctions = {};
35+
const regexp = /<[a-z0-9_]+>/g;
36+
const includedModules = program.match(regexp);
37+
if (!includedModules) {
38+
return allModuleFunctions;
39+
}
40+
for (const m of includedModules) {
41+
const moduleName = m.slice(1, m.length - 1);
42+
43+
if (modulesAvailableForC.has(moduleName)) {
44+
await initModuleContext(moduleName, context, true);
45+
const moduleFuncs = await loadModuleBundle(moduleName, context);
46+
for (const moduleFunc of Object.keys(moduleFuncs)) {
47+
allModuleFunctions[moduleFunc] = moduleFuncs[moduleFunc];
48+
}
49+
}
50+
}
51+
const pixNFlixStart = allModuleFunctions['start'];
52+
allModuleFunctions['start'] = () => {
53+
specialCReturnObject = pixNFlixStart();
54+
};
55+
return allModuleFunctions;
56+
}

yarn.lock

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2314,6 +2314,15 @@
23142314
dependencies:
23152315
"@sinonjs/commons" "^1.7.0"
23162316

2317+
"@sourceacademy/c-slang@^1.0.18":
2318+
version "1.0.18"
2319+
resolved "https://registry.yarnpkg.com/@sourceacademy/c-slang/-/c-slang-1.0.18.tgz#9d9cf94481e3bd12cbe667a3a3872bea59a9d2c4"
2320+
integrity sha512-L4bCfQR1K9EqwaVpIkGt2ADYp/nvludVUj5YOimNsWnE0UbS9Z5WhvdRMm7IyGxGF60B2VAH6BfjiyK8phsIug==
2321+
dependencies:
2322+
bignumber.js "^9.1.2"
2323+
peggy "^3.0.2"
2324+
wabt "^1.0.32"
2325+
23172326
"@sourceacademy/sharedb-ace@^2.0.2":
23182327
version "2.0.2"
23192328
resolved "https://registry.yarnpkg.com/@sourceacademy/sharedb-ace/-/sharedb-ace-2.0.2.tgz#1789fddf947bcd55da39ff76c93b4ecf7382860f"
@@ -4325,6 +4334,11 @@ big.js@^5.2.2:
43254334
resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
43264335
integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
43274336

4337+
bignumber.js@^9.1.2:
4338+
version "9.1.2"
4339+
resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.2.tgz#b7c4242259c008903b13707983b5f4bbd31eda0c"
4340+
integrity sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==
4341+
43284342
binary-extensions@^2.0.0:
43294343
version "2.3.0"
43304344
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522"
@@ -4848,6 +4862,11 @@ comma-separated-tokens@^2.0.0:
48484862
resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz#4e89c9458acb61bc8fef19f4529973b2392839ee"
48494863
integrity sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==
48504864

4865+
commander@^10.0.0:
4866+
version "10.0.1"
4867+
resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06"
4868+
integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==
4869+
48514870
commander@^2.20.0:
48524871
version "2.20.3"
48534872
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
@@ -10077,6 +10096,14 @@ path@^0.12.7:
1007710096
process "^0.11.1"
1007810097
util "^0.10.3"
1007910098

10099+
peggy@^3.0.2:
10100+
version "3.0.2"
10101+
resolved "https://registry.yarnpkg.com/peggy/-/peggy-3.0.2.tgz#07d0578f1ab0ebbac25a202f544a454f37aa3af3"
10102+
integrity sha512-n7chtCbEoGYRwZZ0i/O3t1cPr6o+d9Xx4Zwy2LYfzv0vjchMBU0tO+qYYyvZloBPcgRgzYvALzGWHe609JjEpg==
10103+
dependencies:
10104+
commander "^10.0.0"
10105+
source-map-generator "0.8.0"
10106+
1008010107
performance-now@^2.1.0:
1008110108
version "2.1.0"
1008210109
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
@@ -12189,6 +12216,11 @@ source-list-map@^2.0.0, source-list-map@^2.0.1:
1218912216
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
1219012217
integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==
1219112218

12219+
source-map-generator@0.8.0:
12220+
version "0.8.0"
12221+
resolved "https://registry.yarnpkg.com/source-map-generator/-/source-map-generator-0.8.0.tgz#10d5ca0651e2c9302ea338739cbd4408849c5d00"
12222+
integrity sha512-psgxdGMwl5MZM9S3FWee4EgsEaIjahYV5AzGnwUvPhWeITz/j6rKpysQHlQ4USdxvINlb8lKfWGIXwfkrgtqkA==
12223+
1219212224
"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1, source-map-js@^1.0.2:
1219312225
version "1.0.2"
1219412226
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
@@ -13426,6 +13458,11 @@ w3c-xmlserializer@^2.0.0:
1342613458
dependencies:
1342713459
xml-name-validator "^3.0.0"
1342813460

13461+
wabt@^1.0.32:
13462+
version "1.0.32"
13463+
resolved "https://registry.yarnpkg.com/wabt/-/wabt-1.0.32.tgz#a4611728e67f1c3f7c546fabb7bc4da3e84a67a7"
13464+
integrity sha512-1aHvkKaSrrl7qFtAbQ1RWVHLuJApRh7PtUdYvRtiUEKEhk0MOV0sTuz5cLF6jL5jPLRyifLbZcR65AEga/xBhQ==
13465+
1342913466
walker@^1.0.7:
1343013467
version "1.0.8"
1343113468
resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f"

0 commit comments

Comments
 (0)