[web] Added an option to show execution time on the front end, and improved server.js

This commit is contained in:
Sidi Liang 2024-11-03 17:31:20 +08:00
parent 95031f508b
commit 8ecf309791
No known key found for this signature in database
GPG Key ID: 9785F5EECFFA5311
3 changed files with 66 additions and 14 deletions

View File

@ -11,6 +11,7 @@
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"express": "^4.21.1", "express": "^4.21.1",
"ffi-napi": "^4.0.3" "ffi-napi": "^4.0.3",
"yargs": "^17.7.2"
} }
} }

View File

@ -30,7 +30,7 @@
} }
.logo { .logo {
margin-bottom: 20px; margin-bottom: 2px;
} }
.ascii-art { .ascii-art {
@ -400,6 +400,17 @@
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
line-height: 1.5; line-height: 1.5;
} }
.timing-checkbox {
display: inline-flex;
align-items: center;
margin-left: 10px;
color: #abb2bf;
}
.timing-checkbox input {
margin-right: 5px;
}
</style> </style>
</head> </head>
<body> <body>
@ -449,8 +460,11 @@ println(x);</textarea>
</div> </div>
<div class="controls"> <div class="controls">
<button id="runBtn" onclick="runCode()">Run Code</button> <button id="runBtn" onclick="runCode()">Run</button>
<button id="clearBtn" onclick="clearOutput()">Clear Output</button> <button onclick="clearOutput()">Clear Output</button>
<label class="timing-checkbox">
<input type="checkbox" id="showTime"> Show execution time
</label>
</div> </div>
<div class="examples"> <div class="examples">
@ -557,6 +571,7 @@ for(var i = 0; i <= 5; i += 1) {
const runBtn = document.getElementById('runBtn'); const runBtn = document.getElementById('runBtn');
const output = document.getElementById('output'); const output = document.getElementById('output');
const status = document.getElementById('status'); const status = document.getElementById('status');
const showTime = document.getElementById('showTime').checked;
try { try {
runBtn.disabled = true; runBtn.disabled = true;
@ -567,7 +582,10 @@ for(var i = 0; i <= 5; i += 1) {
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, },
body: JSON.stringify({ code }) body: JSON.stringify({
code,
showTime
})
}); });
const data = await response.json(); const data = await response.json();

View File

@ -1,6 +1,32 @@
const express = require('express'); const express = require('express');
const ffi = require('ffi-napi'); const ffi = require('ffi-napi');
const path = require('path'); const path = require('path');
const yargs = require('yargs/yargs');
const { hideBin } = require('yargs/helpers');
// Parse command line arguments
const argv = yargs(hideBin(process.argv))
.usage('Usage: $0 [options]')
.option('verbose', {
alias: 'v',
type: 'boolean',
description: 'Run with verbose logging'
})
.option('port', {
alias: 'p',
type: 'number',
description: 'Port to run the server on',
default: 3000
})
.option('host', {
type: 'string',
description: 'Host to run the server on',
default: 'localhost'
})
.help()
.alias('help', 'h')
.version()
.argv;
const app = express(); const app = express();
@ -10,41 +36,48 @@ app.use(express.static('public'));
const nasalLib = ffi.Library(path.join(__dirname, '../module/libnasal-web'), { const nasalLib = ffi.Library(path.join(__dirname, '../module/libnasal-web'), {
'nasal_init': ['pointer', []], 'nasal_init': ['pointer', []],
'nasal_cleanup': ['void', ['pointer']], 'nasal_cleanup': ['void', ['pointer']],
'nasal_eval': ['string', ['pointer', 'string']], 'nasal_eval': ['string', ['pointer', 'string', 'int']],
'nasal_get_error': ['string', ['pointer']] 'nasal_get_error': ['string', ['pointer']]
}); });
app.post('/eval', (req, res) => { app.post('/eval', (req, res) => {
const { code } = req.body; const { code, showTime = false } = req.body;
if (!code) { if (!code) {
return res.status(400).json({ error: 'No code provided' }); return res.status(400).json({ error: 'No code provided' });
} }
if (argv.verbose) {
console.log('Received code evaluation request:', code);
console.log('Show time:', showTime);
}
const ctx = nasalLib.nasal_init(); const ctx = nasalLib.nasal_init();
try { try {
const result = nasalLib.nasal_eval(ctx, code); const result = nasalLib.nasal_eval(ctx, code, showTime ? 1 : 0);
const error = nasalLib.nasal_get_error(ctx); const error = nasalLib.nasal_get_error(ctx);
// Check if there's an error first if (error && error !== 'null') {
if (error && error !== 'null' && error.trim() !== '') { if (argv.verbose) console.log('Nasal error:', error);
console.log('Nasal error:', error); // For debugging
res.json({ error: error }); res.json({ error: error });
} else if (result && result.trim() !== '') { } else if (result && result.trim() !== '') {
console.log('Nasal output:', result); // For debugging if (argv.verbose) console.log('Nasal output:', result);
res.json({ result: result }); res.json({ result: result });
} else { } else {
if (argv.verbose) console.log('No output or error returned');
res.json({ error: 'No output or error returned' }); res.json({ error: 'No output or error returned' });
} }
} catch (err) { } catch (err) {
console.error('Server error:', err); // For debugging if (argv.verbose) console.error('Server error:', err);
res.status(500).json({ error: err.message }); res.status(500).json({ error: err.message });
} finally { } finally {
if (argv.verbose) console.log('Cleaning up Nasal context');
nasalLib.nasal_cleanup(ctx); nasalLib.nasal_cleanup(ctx);
} }
}); });
const PORT = process.env.PORT || 3000; const PORT = argv.port || 3000;
app.listen(PORT, () => { app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`); console.log(`Server running on port ${PORT}`);
console.log(`Visit http://localhost:${PORT} to use the Nasal interpreter`); console.log(`Visit http://localhost:${PORT} to use the Nasal interpreter`);
if (argv.verbose) console.log('Verbose logging enabled');
}); });