Initial commit

This commit is contained in:
uohlhv 2021-09-16 20:45:54 +08:00
commit b7defdf5d8
35 changed files with 2343 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
**/.DS_Store
**/main.js
**/node_modules

190
Home/.stylelintrc Normal file
View File

@ -0,0 +1,190 @@
{
"extends": "/usr/local/lib/node_modules/stylelint-config-standard",
"plugins": [
"/usr/local/lib/node_modules/stylelint-order"
],
"rules": {
"at-rule-no-vendor-prefix": true,
"media-feature-name-no-vendor-prefix": true,
"property-no-vendor-prefix": true,
"selector-no-vendor-prefix": true,
"value-no-vendor-prefix": true,
"order/properties-order": [
[
"font",
"font-family",
"font-size",
"font-weight",
"font-style",
"font-variant",
"font-size-adjust",
"font-stretch",
"font-effect",
"font-emphasize",
"font-emphasize-position",
"font-emphasize-style",
"font-smooth",
"line-height",
"position",
"z-index",
"top",
"right",
"bottom",
"left",
"display",
"visibility",
"float",
"clear",
"overflow",
"overflow-x",
"overflow-y",
"clip",
"zoom",
"align-content",
"align-items",
"align-self",
"flex",
"flex-flow",
"flex-basis",
"flex-direction",
"flex-grow",
"flex-shrink",
"flex-wrap",
"justify-content",
"order",
"box-sizing",
"width",
"min-width",
"max-width",
"height",
"min-height",
"max-height",
"margin",
"margin-top",
"margin-right",
"margin-bottom",
"margin-left",
"padding",
"padding-top",
"padding-right",
"padding-bottom",
"padding-left",
"table-layout",
"empty-cells",
"caption-side",
"border-spacing",
"border-collapse",
"list-style",
"list-style-position",
"list-style-type",
"list-style-image",
"content",
"quotes",
"counter-reset",
"counter-increment",
"resize",
"cursor",
"user-select",
"nav-index",
"nav-up",
"nav-right",
"nav-down",
"nav-left",
"transition",
"transition-delay",
"transition-timing-function",
"transition-duration",
"transition-property",
"transform",
"transform-origin",
"animation",
"animation-name",
"animation-duration",
"animation-play-state",
"animation-timing-function",
"animation-delay",
"animation-iteration-count",
"animation-direction",
"text-align",
"text-align-last",
"vertical-align",
"white-space",
"text-decoration",
"text-emphasis",
"text-emphasis-color",
"text-emphasis-style",
"text-emphasis-position",
"text-indent",
"text-justify",
"letter-spacing",
"word-spacing",
"text-outline",
"text-transform",
"text-wrap",
"text-overflow",
"text-overflow-ellipsis",
"text-overflow-mode",
"word-wrap",
"word-break",
"tab-size",
"hyphens",
"pointer-events",
"opacity",
"color",
"border",
"border-width",
"border-style",
"border-color",
"border-top",
"border-top-width",
"border-top-style",
"border-top-color",
"border-right",
"border-right-width",
"border-right-style",
"border-right-color",
"border-bottom",
"border-bottom-width",
"border-bottom-style",
"border-bottom-color",
"border-left",
"border-left-width",
"border-left-style",
"border-left-color",
"border-radius",
"border-top-left-radius",
"border-top-right-radius",
"border-bottom-right-radius",
"border-bottom-left-radius",
"border-image",
"border-image-source",
"border-image-slice",
"border-image-width",
"border-image-outset",
"border-image-repeat",
"outline",
"outline-width",
"outline-style",
"outline-color",
"outline-offset",
"background",
"background-color",
"background-image",
"background-repeat",
"background-attachment",
"background-position",
"background-position-x",
"background-position-y",
"background-clip",
"background-origin",
"background-size",
"box-decoration-break",
"box-shadow",
"text-shadow"
],
{
"unspecified": "bottomAlphabetical"
}
]
}
}

3
Home/Compiler/.babelrc Normal file
View File

@ -0,0 +1,3 @@
{
"presets": ["@babel/preset-env"]
}

1
Home/Compiler/.npmrc Normal file
View File

@ -0,0 +1 @@
package-lock=false

190
Home/Compiler/.stylelintrc Normal file
View File

@ -0,0 +1,190 @@
{
"extends": "/usr/local/lib/node_modules/stylelint-config-standard",
"plugins": [
"/usr/local/lib/node_modules/stylelint-order"
],
"rules": {
"at-rule-no-vendor-prefix": true,
"media-feature-name-no-vendor-prefix": true,
"property-no-vendor-prefix": true,
"selector-no-vendor-prefix": true,
"value-no-vendor-prefix": true,
"order/properties-order": [
[
"font",
"font-family",
"font-size",
"font-weight",
"font-style",
"font-variant",
"font-size-adjust",
"font-stretch",
"font-effect",
"font-emphasize",
"font-emphasize-position",
"font-emphasize-style",
"font-smooth",
"line-height",
"position",
"z-index",
"top",
"right",
"bottom",
"left",
"display",
"visibility",
"float",
"clear",
"overflow",
"overflow-x",
"overflow-y",
"clip",
"zoom",
"align-content",
"align-items",
"align-self",
"flex",
"flex-flow",
"flex-basis",
"flex-direction",
"flex-grow",
"flex-shrink",
"flex-wrap",
"justify-content",
"order",
"box-sizing",
"width",
"min-width",
"max-width",
"height",
"min-height",
"max-height",
"margin",
"margin-top",
"margin-right",
"margin-bottom",
"margin-left",
"padding",
"padding-top",
"padding-right",
"padding-bottom",
"padding-left",
"table-layout",
"empty-cells",
"caption-side",
"border-spacing",
"border-collapse",
"list-style",
"list-style-position",
"list-style-type",
"list-style-image",
"content",
"quotes",
"counter-reset",
"counter-increment",
"resize",
"cursor",
"user-select",
"nav-index",
"nav-up",
"nav-right",
"nav-down",
"nav-left",
"transition",
"transition-delay",
"transition-timing-function",
"transition-duration",
"transition-property",
"transform",
"transform-origin",
"animation",
"animation-name",
"animation-duration",
"animation-play-state",
"animation-timing-function",
"animation-delay",
"animation-iteration-count",
"animation-direction",
"text-align",
"text-align-last",
"vertical-align",
"white-space",
"text-decoration",
"text-emphasis",
"text-emphasis-color",
"text-emphasis-style",
"text-emphasis-position",
"text-indent",
"text-justify",
"letter-spacing",
"word-spacing",
"text-outline",
"text-transform",
"text-wrap",
"text-overflow",
"text-overflow-ellipsis",
"text-overflow-mode",
"word-wrap",
"word-break",
"tab-size",
"hyphens",
"pointer-events",
"opacity",
"color",
"border",
"border-width",
"border-style",
"border-color",
"border-top",
"border-top-width",
"border-top-style",
"border-top-color",
"border-right",
"border-right-width",
"border-right-style",
"border-right-color",
"border-bottom",
"border-bottom-width",
"border-bottom-style",
"border-bottom-color",
"border-left",
"border-left-width",
"border-left-style",
"border-left-color",
"border-radius",
"border-top-left-radius",
"border-top-right-radius",
"border-bottom-right-radius",
"border-bottom-left-radius",
"border-image",
"border-image-source",
"border-image-slice",
"border-image-width",
"border-image-outset",
"border-image-repeat",
"outline",
"outline-width",
"outline-style",
"outline-color",
"outline-offset",
"background",
"background-color",
"background-image",
"background-repeat",
"background-attachment",
"background-position",
"background-position-x",
"background-position-y",
"background-clip",
"background-origin",
"background-size",
"box-decoration-break",
"box-shadow",
"text-shadow"
],
{
"unspecified": "bottomAlphabetical"
}
]
}
}

24
Home/Compiler/LICENSE Normal file
View File

@ -0,0 +1,24 @@
The MIT License (MIT)
Copyright (c) 2011-2019 Twitter, Inc.
Copyright (c) 2011-2019 The Bootstrap Authors
Copyright (C) 2017 by Marijn Haverbeke <marijnh@gmail.com> and others
Copyright (c) 2013-present, Yuxi (Evan) You
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

22
Home/Compiler/README.md Normal file
View File

@ -0,0 +1,22 @@
# Requirements
- node.js
- npm
# Install Dependencies
```
$ npm install
```
# Generate main.js
```
$ npm run build
```
# Watch for changes
```
$ npm run watch
```

75
Home/Compiler/index.html Normal file
View File

@ -0,0 +1,75 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Assignment</title>
<link rel="stylesheet" href="lib/bootstrap.min.css">
<link rel="stylesheet" href="lib/codemirror.min.css">
<link rel="stylesheet" href="main.css">
</head>
<body>
<div id="root" class="container">
<h1>Lexical, Syntax & Semantic Analysis</h1>
<dl class="row mt-5">
<dt class="col-sm-2">Requirements: </dt>
<dd class="col-sm-10">
<pre>
# 保留字或運算子begin, for, end, if, then, else, while, int
# 運算子:+, -, *, /, =
# 判斷子:<, >, <=, >=, ==, and, or
# 若有詞法或語法錯誤,請印出並說明
# 請印出所有變數最後的狀態
# 自定義文法,使得以下範例成立:
begin
int x=0, zzz=5;
int i, j=1;
# 進行迴圈
for, i=1, i<=50, j; # j爲i每次遞增的值
x=x+1;
end
if ((x>5) and (j<=8)) then
x=1;
else
for, i=1, i<=50, j+1;
x=x+1;
end
while (i<11)
yy=x;
i=i+1;
end
end
end
</pre>
</dd>
<dt class="col-sm-2">Source Code: </dt>
<dd class="col-sm-10">
<div class="custom-file">
<input type="file" id="file" class="custom-file-input" @change="fileHandler">
<label class="custom-file-label" for="file">{{ file ? file.name : 'Choose file' }}</label>
<button type="button" class="btn btn-secondary" @click="fileHandler">Reload</button>
</div>
<div id="editor" class="editor"></div>
</dd>
<dt class="col-sm-2"></dt>
<dd class="col-sm-10">
<button type="button" class="btn btn-info" @click="lexer">Lex</button>
<button type="button" class="btn btn-info" @click="parser">Parse</button>
<button type="button" class="btn btn-primary" @click="run">Run</button>
<button type="button" class="btn btn-secondary" @click="reset">Clear</button>
</dd>
<hr>
<dt class="col-sm-2">Console: </dt>
<dd class="col-sm-10">
<pre class="error" v-if="error" v-text="error"></pre>
<pre v-if="!error && ids.length" v-for="(v, i) in ids">{{ v }}: {{ vars[i] }}</pre>
<p v-if="!error && lex.length" v-for="(l, i) in lex">{{ l }}</p>
<pre v-if="!error && json" v-text="json"></pre>
</dd>
</dl>
</div>
<script src="lib/codemirror.min.js"></script>
<script src="lib/vue-2.6.0.min.js"></script>
<script src="main.js"></script>
</body>
</html>

7
Home/Compiler/lib/bootstrap.min.css vendored Normal file

File diff suppressed because one or more lines are too long

8
Home/Compiler/lib/codemirror.min.css vendored Normal file

File diff suppressed because one or more lines are too long

8
Home/Compiler/lib/codemirror.min.js vendored Normal file

File diff suppressed because one or more lines are too long

6
Home/Compiler/lib/vue-2.6.0.min.js vendored Normal file

File diff suppressed because one or more lines are too long

30
Home/Compiler/main.css Normal file
View File

@ -0,0 +1,30 @@
h1 {
margin-top: 50px;
}
pre {
font-family: "Menlo", "Consolas", monospace;
}
pre.error {
color: red;
}
hr {
width: 100%;
}
.custom-file {
width: 250px;
}
.custom-file button {
position: relative;
top: -38px;
left: 255px;
}
.editor {
margin: 20px 0;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
}

View File

@ -0,0 +1,20 @@
{
"name": "Compiler",
"version": "1.0.0",
"description": "",
"main": "main.js",
"scripts": {
"build": "webpack",
"watch": "npm run build -- --watch"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.7.2",
"@babel/preset-env": "^7.7.1",
"babel-loader": "^8.0.6",
"webpack": "^4.41.2",
"webpack-cli": "^3.3.10"
}
}

View File

@ -0,0 +1,6 @@
<!DOCTYPE html>
<head>
<title>sandbox</title>
</head>
<body>
</body>

View File

@ -0,0 +1,571 @@
/* global Vue FileReader CodeMirror */
Vue.config.devtools = true
window.app = new Vue({
el: '#root',
data: {
ast: {},
editor: {},
error: '',
fileReader: new FileReader(),
file: '',
ids: [],
js: '',
json: '',
lex: [],
vars: [] // the value of ids
},
mounted () {
this.editor = CodeMirror(document.querySelector('#editor'), {
lineNumbers: true,
tabSize: 2,
extraKeys: {
Tab: cm => {
const spaces = Array(cm.getOption('indentUnit') + 1).join(' ')
cm.replaceSelection(spaces)
}
}
})
this.fileReader.onload = e => {
this.editor.setValue(this.fileReader.result)
}
},
methods: {
fileHandler () {
const file = document.querySelector('#file').files[0]
if (file) this.file = file
this.fileReader.readAsText(this.file)
},
reset () {
this.error = ''
this.ids = []
this.vars = []
this.lex = []
this.json = ''
},
run () {
try {
this.reset()
this.ast = parse(TokenStream(InputStream(this.editor.getValue())))
this.js = '(function () { "use strict"; try { ' + makeJS(this.ast)
this.ids = [...new Set(this.ids)]
this.js +=
' parent.app.vars = [' +
this.ids.join(', ') +
']; } catch (e) { parent.app.error = "Runtime Error: " + e.message; }}());'
// recreate an iframe sandbox if it already exist
// , then run the transpiled js in it
const sb = document.querySelector('#sandbox')
if (sb) sb.remove()
const iframe = document.createElement('iframe')
iframe.id = 'sandbox'
iframe.style.display = 'none'
iframe.src = 'sandbox.html'
document.body.appendChild(iframe)
const script = document.createElement('script')
script.textContent = this.js
document
.querySelector('#sandbox')
.contentDocument.body.appendChild(script)
} catch (e) {
this.error = e.message
}
},
lexer () {
try {
this.reset()
const tokens = TokenStream(InputStream(this.editor.getValue()))
while (!tokens.eof()) {
this.lex.push(tokens.next())
}
} catch (e) {
this.error = e.message
}
},
parser () {
try {
this.reset()
this.ast = parse(TokenStream(InputStream(this.editor.getValue())))
this.json = JSON.stringify(this.ast, null, 2)
} catch (e) {
this.error = e.message
}
}
}
})
function InputStream (input) {
let pos = 0
let line = 1
let col = 0
return {
next: next,
peek: peek,
eof: eof,
croak: croak
}
function next () {
const ch = input.charAt(pos++)
if (ch === '\n') {
line++
col = 0
} else col++
return ch
}
function peek () {
return input.charAt(pos)
}
function eof () {
return peek() === ''
}
function croak (msg, type = 'Syntax') {
throw new Error(type + ' Error: ' + msg + ' (' + line + ':' + col + ')')
}
}
function TokenStream (input) {
let current = null
const reserved =
' abstract arguments boolean break byte case catch char class const continue' +
' debugger default delete do double else enum eval export extends false final' +
' finally float for function goto if implements import in instanceof int' +
' interface let long native new null package private protected public return' +
' short static super switch synchronized this throw throws transient true try' +
' typeof var void volatile while with yield '
const keywords = ' begin for end if then else while int '
const ops = ' + - * / = == != > < >= <= and or '
return {
next: next,
peek: peek,
eof: eof,
croak: input.croak
}
function isKeyword (x) {
return keywords.indexOf(' ' + x + ' ') >= 0
}
function isReservedWord (x) {
return reserved.indexOf(' ' + x + ' ') >= 0
}
function isDigit (ch) {
return /[0-9]/i.test(ch)
}
function isIdStart (ch) {
return /[a-z_]/i.test(ch)
}
function isId (ch) {
return isIdStart(ch) || '_0123456789'.indexOf(ch) >= 0
}
function isOpChar (ch, prev, prev2) {
if (!prev) return '+-*/=!<>'.indexOf(ch) >= 0
else if (
(prev === '=' && !prev2) ||
prev === '!' ||
prev === '<' ||
prev === '>'
) {
return ch === '='
}
return false
}
function isOp (x) {
return ops.indexOf(' ' + x + ' ') >= 0
}
function isPunc (ch) {
return ',;()'.indexOf(ch) >= 0
}
function isWhitespace (ch) {
return ' \t\n'.indexOf(ch) >= 0
}
function readWhile (predicate) {
let str = ''
while (
!input.eof() &&
predicate(input.peek(), str[str.length - 1], str[str.length - 2])
) {
str += input.next()
}
return str
}
function readNumber () {
const number = readWhile(isDigit)
return { type: 'num', value: parseInt(number, 10) }
}
function readIdent () {
const id = readWhile(isId)
if (!isKeyword(id) && isReservedWord(id)) {
input.croak('Variable name "' + id + '" is not allowed', 'Lexical')
}
return {
type: isKeyword(id) ? 'kw' : isOp(id) ? 'op' : 'var',
value: id
}
}
function readOp () {
const id = readWhile(isOpChar)
return {
type: isOp(id) ? 'op' : input.croak('Unknown symbol: ' + id, 'Lexical'),
value: id
}
}
function skipComment () {
readWhile(function (ch) {
return ch !== '\n'
})
input.next()
}
function readNext () {
readWhile(isWhitespace)
if (input.eof()) return null
const ch = input.peek()
if (ch === '#') {
skipComment()
return readNext()
}
if (isDigit(ch)) return readNumber()
if (isPunc(ch)) {
return {
type: 'punc',
value: input.next()
}
}
if (isOpChar(ch)) return readOp()
if (isIdStart(ch)) return readIdent()
input.croak('Invalid symbol: ' + ch, 'Lexical')
}
function peek () {
return current || (current = readNext())
}
function next () {
const tok = current
current = null
return tok || readNext()
}
function eof () {
return peek() === null
}
}
function parse (input) {
const PRECEDENCE = {
'=': 1,
or: 2,
and: 3,
'<': 7,
'>': 7,
'<=': 7,
'>=': 7,
'==': 7,
'!=': 7,
'+': 10,
'-': 10,
'*': 20,
'/': 20
}
return parseToplevel()
function parseToplevel () {
const prog = []
while (!input.eof()) {
prog.push(parseExpression())
const last = prog.slice(-1)[0]
if (requireSemicolon(last)) skipPunc(';')
}
return { type: 'prog', prog: prog }
}
function parseBlock () {
const body = []
while (!input.eof() && !isKw('else') && !isKw('end')) {
body.push(parseExpression())
const last = body.slice(-1)[0]
if (requireSemicolon(last)) skipPunc(';')
}
return { type: 'block', body: body }
}
function parseExpression () {
if (input.eof()) input.croak('Expecting an expression but reached EOF')
return maybeBinary(parseAtom(), 0)
}
function parseAtom () {
if (isPunc('(')) {
input.next()
const exp = parseExpression()
skipPunc(')')
return exp
}
if (isKw('begin')) return parseBegin()
if (isKw('int')) return parseInit()
if (isKw('for')) return parseFor()
if (isKw('while')) return parseWhile()
if (isKw('if')) return parseIf()
if (isOp('-')) return parseUnary('-')
if (isOp('+')) return parseUnary('+')
const tok = input.next()
if (tok.type === 'var' || tok.type === 'num') {
return tok
}
input.croak('Unexpected token ' + tok.value)
}
function parseBegin () {
skipKw('begin')
const block = parseBlock()
skipKw('end')
return { type: 'begin', body: block.body }
}
function parseInit () {
const vars = []
let first = true
skipKw('int')
while (!input.eof()) {
if (isPunc(';')) break
if (first) first = false
else skipPunc(',')
if (isPunc(';')) break // the last "," can be missing
const tok = parseExpression()
if (tok.type !== 'var' && tok.type !== 'assign') {
input.croak('Unexpected token ' + tok.type)
}
vars.push(tok)
}
skipPunc(';')
return {
type: 'init',
vars: vars
}
}
function parseFor () {
skipKw('for')
skipPunc(',')
const init = parseExpression()
if (init.type !== 'assign') {
input.croak('Unexpected token ' + init.type)
}
skipPunc(',')
const cond = parseExpression()
isRval(cond)
skipPunc(',')
const inc = parseExpression()
isRval(inc)
skipPunc(';')
const body = parseBlock()
skipKw('end')
return {
type: 'for',
init: init,
cond: cond,
inc: inc,
body: body
}
}
function parseWhile () {
skipKw('while')
skipPunc('(')
const cond = parseExpression()
isRval(cond)
skipPunc(')')
const body = parseBlock()
skipKw('end')
return { type: 'while', cond: cond, body: body }
}
function parseIf () {
skipKw('if')
skipPunc('(')
const cond = parseExpression()
isRval(cond)
skipPunc(')')
skipKw('then')
const then = parseBlock()
const ret = { type: 'if', cond: cond, then: then }
if (isKw('else')) {
input.next()
ret.else = parseBlock()
}
skipKw('end')
return ret
}
function parseUnary (op) {
skipOp(op)
const right = parseAtom()
isRval(right)
return { type: 'unary', operator: op, right: right }
}
function maybeBinary (left, myPrec) {
if (left.type === 'assign' && left.left.type !== 'var') {
input.croak('Invalid left-hand side in assignment')
}
const tok = isOp()
if (tok) {
const hisPrec = PRECEDENCE[tok.value]
if (hisPrec > myPrec) {
input.next()
const right = maybeBinary(parseAtom(), hisPrec)
isRval(left)
isRval(right)
return maybeBinary(
{
type: tok.value === '=' ? 'assign' : 'binary',
operator: tok.value,
left: left,
right: right
},
myPrec
)
}
}
return left
}
function isPunc (ch) {
var tok = input.peek()
return tok && tok.type === 'punc' && (!ch || tok.value === ch) && tok
}
function isKw (kw) {
var tok = input.peek()
return tok && tok.type === 'kw' && (!kw || tok.value === kw) && tok
}
function isOp (op) {
var tok = input.peek()
return tok && tok.type === 'op' && (!op || tok.value === op) && tok
}
function isRval (tok) {
if (
tok.type !== 'var' &&
tok.type !== 'num' &&
tok.type !== 'unary' &&
tok.type !== 'binary'
) {
input.croak('Unexpected token ' + tok.type)
}
}
function skipOp (ch) {
if (isOp(ch)) input.next()
else if (!input.eof()) {
input.croak('Expecting ' + ch + ' but got ' + input.peek().value)
} else {
input.croak('Expecting ' + ch + ' but reached EOF')
}
}
function skipPunc (ch) {
if (isPunc(ch)) input.next()
else if (!input.eof()) {
input.croak('Expecting ' + ch + ' but got ' + input.peek().value)
} else {
input.croak('Expecting ' + ch + ' but reached EOF')
}
}
function skipKw (kw) {
if (isKw(kw)) input.next()
else if (!input.eof()) {
input.croak('Expecting ' + kw + ' but got ' + input.peek().value)
} else {
input.croak('Expecting ' + kw + ' but reached EOF')
}
}
function isBlock (tok) {
return (
tok.type === 'begin' ||
tok.type === 'for' ||
tok.type === 'while' ||
tok.type === 'if'
)
}
function requireSemicolon (tok) {
return !isBlock(tok) && tok.type !== 'init'
}
}
function makeJS (ast) {
return js(ast)
function js (ast) {
const cases = [
'js_num',
'js_var',
'js_unary',
'js_binary',
'js_assign',
'js_init',
'js_begin',
'js_block',
'js_prog',
'js_for',
'js_while',
'js_if'
]
const handler = {
js_num (ast) {
return JSON.stringify(ast.value)
},
js_var (ast) {
window.app.ids.push(ast.value)
return ast.value
},
js_unary (ast) {
const op = ast.operator
return '( ' + op + js(ast.right) + ' )'
},
js_binary (ast) {
let op = ' ' + ast.operator + ' '
if (op === ' and ') op = ' && '
if (op === ' or ') op = ' || '
if (op === ' / ') {
return 'Math.trunc(' + js(ast.left) + op + js(ast.right) + ')'
}
return '( ' + js(ast.left) + op + js(ast.right) + ' )'
},
js_assign (ast) {
return js(ast.left) + ' = ' + js(ast.right)
},
js_init (ast) {
const vars = []
for (let i = 0; i < ast.vars.length; i += 1) {
vars.push(js(ast.vars[i]))
}
return 'var ' + vars.join(', ')
},
js_prog (ast) {
const prog = []
for (let i = 0; i < ast.prog.length; i += 1) {
prog.push(js(ast.prog[i]))
}
return prog.join('; ') + ';'
},
js_block (ast) {
const body = []
for (let i = 0; i < ast.body.length; i += 1) {
body.push(js(ast.body[i]))
}
return body.join('; ') + ';'
},
js_begin (ast) {
return this.js_block(ast)
},
js_for (ast) {
return (
'for (' +
js(ast.init) +
'; ' +
js(ast.cond) +
'; ' +
js(ast.init.left) +
' += ' +
js(ast.inc) +
') { ' +
js(ast.body) +
' }'
)
},
js_while (ast) {
return 'while (' + js(ast.cond) + ') { ' + js(ast.body) + ' }'
},
js_if (ast) {
let str = 'if (' + js(ast.cond) + ') { ' + js(ast.then) + ' }'
if (ast.else) {
str += ' else { ' + js(ast.else) + ' }'
}
return str
}
}
for (let i = 0; i < cases.length; i += 1) {
if ('js_' + ast.type === cases[i]) {
return handler[cases[i]](ast)
}
}
}
}

View File

@ -0,0 +1,24 @@
begin
int x=0, zzz = 5;
int i, j = 1;
# 進行迴圈計算
for, i=1, i<=50, j; # j是i每次增加的數值
x=x+1;
end # for
if ((x>5) and (j<=8)) then
x=1;
else
for, i=1, i<=50, j+1;
x=x+1;
end
while (i < 11)
yy = x;
i = i + 1;
end
end # if
end

View File

@ -0,0 +1,24 @@
begin
int x=0, zzz = 5;
int i, j = 1;
# 進行迴圈計算
for, i=1, i<=50, j; # j是i每次增加的數值
x=x+1;
end # for
if ((x>5) and (j<=8)) then
x=1;
else
for, i=1, i<=50, j+1;
x=x+1;
end
while (i < 11)
yy = x;
i = i + 1;
end
end # if
end

View File

@ -0,0 +1,13 @@
const path = require('path')
module.exports = {
mode: 'none',
entry: './src/main.dev.js',
output: {
path: path.resolve(__dirname, './'),
filename: 'main.js'
},
module: {
rules: [{ test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader' }]
}
}

BIN
Home/LTI/asset/clap.mp3 Normal file

Binary file not shown.

BIN
Home/LTI/asset/sample.mp3 Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

201
Home/LTI/index.html Normal file
View File

@ -0,0 +1,201 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>LTI System Analysis</title>
<link rel="stylesheet" href="lib/bootstrap.min.css">
<link rel="stylesheet" href="main.css">
<script src="lib/plotlyjs.min.js"></script>
</head>
<body>
<div id="root" class="container">
<h1 class="mt-3">LTI System Analysis</h1>
<h3 class="code mt-5">
<span class="coef">a<sub>0</sub></span>y[n] +
<span class="coef">a<sub>1</sub></span>y[n-1] +
<span class="coef">a<sub>2</sub></span>y[n-2] + ... +
<span class="coef">a<sub>p</sub></span>y[n-<span class="deg">p</span>] =
</h3>
<h3 class="code text-right">
<span class="coef">b<sub>0</sub></span>x[n] +
<span class="coef">b<sub>1</sub></span>x[n-1] +
<span class="coef">b<sub>2</sub></span>x[n-2] + ... +
<span class="coef">b<sub>q</sub></span>x[n-<span class="deg">q</span>]
</h3>
<dl class="row mt-5">
<dt class="col-sm-2">JSON data:</dt>
<dd class="col-sm-10">
<div class="custom-file">
<input type="file" id="systemFile" class="custom-file-input" @change="systemFileHandler">
<label class="custom-file-label" for="systemFile">{{ systemFile ? systemFile.name : 'Choose file' }}</label>
<button type="button" class="btn btn-secondary" @click="systemFileHandler">Reload</button>
</div>
</dd>
<dd class="col-sm-12 maxheight">
<dl class="row mt-0 mb-0">
<dt class="col-sm-2">Orders:</dt>
<dd class="code col-sm-5">
<label>p<sub>&nbsp;</sub> =</label>
<input type="number" min="0" v-model.number="p" @change="system('a')">
</dd>
<dd class="code col-sm-5">
<label>q<sub>&nbsp;</sub> =</label>
<input type="number" min="0" v-model.number="q" @change="system('b')">
</dd>
<dt class="col-sm-2">Coefficients:</dt>
<dd class="code col-sm-5">
<p v-for="(c, i) in a">
<label>a<sub>{{ i }}</sub> =</label>
<input type="number" v-model.number="a[i]" @change="output">
</p>
</dd>
<dd class="code col-sm-5">
<p v-for="(c, i) in b">
<label>b<sub>{{ i }}</sub> =</label>
<input type="number" v-model.number="b[i]" @change="output">
</p>
</dd>
</dl>
</dd>
<dt class="col-sm-2">System:</dt>
<dd class="code col-sm-10">
<span v-for="(c, i) in a">
<span v-if="!firstTerm(a, i) && c > 0">+</span>
<span v-else-if="c < 0">-</span>
<span v-if="c == 1 || c == -1">
y[n{{ i ? '-' + i : '' }}]
</span>
<span v-else-if="c > 0">
{{ c }}y[n{{ i ? '-' + i : '' }}]
</span>
<span v-else-if="c < 0">
{{ Math.abs(c) }}y[n{{ i ? '-' + i : '' }}]
</span>
</span>
<span v-if="allZero(a)">0</span>
=
<span v-for="(c, i) in b">
<span v-if="!firstTerm(b, i) && c > 0">+</span>
<span v-else-if="c < 0">-</span>
<span v-if="c == 1 || c == -1">
x[n{{ i ? '-' + i : '' }}]
</span>
<span v-else-if="c > 0">
{{ c }}x[n{{ i ? '-' + i : '' }}]
</span>
<span v-else-if="c < 0">
{{ Math.abs(c) }}x[n{{ i ? '-' + i : '' }}]
</span>
</span>
<span v-if="allZero(b)">0</span>
</dd>
<hr class="divider">
<dt class="col-sm-12 mb-4"><h5>Time Response Simulation:</h5></dt>
<dt class="col-sm-2">Plot mode:</dt>
<dd class="col-sm-10">
<input id="stem" type="checkbox" v-model="stem" @change="output">
<label for="stem">stem</label>
</dd>
<dt class="col-sm-2">Input preset:</dt>
<dd class="code col-sm-3">
<select v-model="preset" @change="presetHandler">
<option value="d">δ[n]</option>
<option value="u">u[n]</option>
<option value="sin">Asin[Ωn+Φ]</option>
<option value="cos">Acos[Ωn+Φ]</option>
<option value="exp">exp[n]</option>
<option value="log">log[n+1]</option>
<option value="n">n</option>
<option value="c">custom</option>
</select>
</dd>
<dt class="col-sm-2">Input length: </dt>
<dd class="code col-sm-5">
<input type="number" min="1" v-model.number="length" @change="lengthHandler">
</dd>
<dt v-if="isTrig" class="col-sm-2">A:</dt>
<dd v-if="isTrig" class="code col-sm-10">
<input type="number" v-model.number="A" @change="trigHandler">
</dd>
<dt v-if="isTrig" class="col-sm-2">Ω:</dt>
<dd v-if="isTrig" class="code col-sm-10">
2π / <input type="number" min="1" v-model.number="T" @change="trigHandler">
</dd>
<dt v-if="isTrig" class="col-sm-2">Φ:</dt>
<dd v-if="isTrig" class="code col-sm-10">
Ω × <input type="number" v-model.number="tao" @change="trigHandler">
</dd>
<dt class="col-sm-2">Input scaler:</dt>
<dd class="code col-sm-3">
<input type="number" v-model.number="scaler" @change="output" :disabled="!readonly">
</dd>
<dt class="col-sm-3">Raise scaler to the power of n: </dt>
<dd class="col-sm-4">
<input type="checkbox" v-model="toPower" @change="output" :disabled="!readonly">
</dd>
<dt class="col-sm-2"></dt>
<dd class="col-sm-3"></dd>
<dt class="col-sm-3">Reverse: </dt>
<dd class="col-sm-4">
<input type="checkbox" v-model="reverse" @change="output">
</dd>
<dt class="col-sm-2"></dt>
<dd class="code col-sm-10"></dd>
<dt class="col-sm-2">Input:</dt>
<dd class="code col-sm-10">
<input style="width: 100%" v-model="x" @change="inputHandler" :readonly="readonly">
</dd>
<div id="input"></div>
<hr class="divider">
<dt class="col-sm-2">Output:</dt>
<dd class="code col-sm-10">
<p class="nowrap" v-text="y.join(',')"></p>
</dd>
<div id="output"></div>
<hr class="divider">
<dt class="col-sm-10 mb-3"><h5>Frequency Response Analysis:</h5></dt>
<dd class="col-sm-2">
<button class="btn btn-success float-right" @click="redrawFR">Replot</button>
</dd>
<div id="magnitude"></div>
<div id="phase"></div>
<hr class="divider">
<dt class="col-sm-10 mb-3"><h5>Convolution Reverb Demo:</h5></dt>
<dd class="col-sm-2"></dd>
<dt class="col-sm-2">Clap response:</dt>
<dd class="col-sm-10">
<div>
<iframe width="480" height="270" src="https://www.youtube.com/embed/W3hsOFazEz4?rel=0" frameborder="0"></iframe>
</div>
<div>
<img src="asset/wavform-clap.png" alt="clap">
</div>
<audio controls>
<source src="asset/clap.mp3" type="audio/mpeg">
Your browser does not support the audio tag.
</audio>
</dd>
<dt class="col-sm-2">Sample input:</dt>
<dd class="col-sm-10">
<div>
<img src="asset/wavform-sample.png" alt="sample">
</div>
<audio controls>
<source src="asset/sample.mp3" type="audio/mpeg">
Your browser does not support the audio tag.
</audio>
</dd>
<dt class="col-sm-2">Result:</dt>
<dd class="col-sm-10">
<div>
<canvas id="waveform3" width="480" height="100"></canvas>
</div>
<button class="btn btn-primary" @click="reverb">Reverb</button>
</dd>
</dl>
</div>
<script src="lib/math.min.js"></script>
<script src="lib/vue.min.js"></script>
<script src="main.js"></script>
</body>
</html>

7
Home/LTI/lib/bootstrap.min.css vendored Normal file

File diff suppressed because one or more lines are too long

43
Home/LTI/lib/math.min.js vendored Normal file

File diff suppressed because one or more lines are too long

24
Home/LTI/lib/plotlyjs.min.js vendored Normal file

File diff suppressed because one or more lines are too long

6
Home/LTI/lib/vue.min.js vendored Normal file

File diff suppressed because one or more lines are too long

49
Home/LTI/main.css Normal file
View File

@ -0,0 +1,49 @@
input {
padding: 0 0.5em;
}
.deg {
color: rgb(192, 0, 0);
}
.coef {
color: rgb(0, 0, 192);
}
.code {
font-family: "Menlo", "Consolas", monospace;
}
.code p {
margin: 0;
}
.nowrap {
overflow: auto;
white-space: nowrap;
}
.divider {
width: 100%;
margin-bottom: 1.8rem;
}
.custom-file {
width: 300px;
}
.custom-file button {
position: relative;
top: -38px;
left: 305px;
}
.maxheight {
overflow-y: auto;
width: 100%;
max-height: 300px;
}
audio:focus {
outline: none;
}

20
Home/LTI/package.json Normal file
View File

@ -0,0 +1,20 @@
{
"name": "LTI",
"version": "1.0.0",
"description": "",
"main": "main.js",
"scripts": {
"build": "webpack",
"watch": "npm run build -- --watch"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.7.2",
"@babel/preset-env": "^7.7.1",
"babel-loader": "^8.0.6",
"webpack": "^4.41.2",
"webpack-cli": "^3.3.10"
}
}

706
Home/LTI/src/main.dev.js Normal file
View File

@ -0,0 +1,706 @@
/* global
Audio,
AudioContext,
FileReader,
Plotly,
Vue,
cancelAnimationFrame,
fetch,
math,
requestAnimationFrame
*/
Vue.config.devtools = true
const figureIn = {
data: [
{
line: {
dash: 'solid',
color: 'rgb(0,113.985,188.955)',
width: 1
},
mode: 'lines+markers',
name: '',
type: 'scatter',
x: [],
y: [],
xaxis: 'x',
yaxis: 'y',
marker: {
line: {
width: 1,
color: []
},
size: 8,
symbol: 'circle',
color: []
},
visible: true,
showlegend: true
}
],
layout: {
title: '',
width: 840,
xaxis: {
side: 'bottom',
type: 'linear',
range: [],
ticks: 'inside',
anchor: 'y',
domain: [0.13, 0.905],
mirror: 'ticks',
nticks: 9,
ticklen: 6.51,
showgrid: false,
showline: true,
tickfont: {
size: 10,
color: 'rgb(38.25,38.25,38.25)',
family: 'Arial, sans-serif'
},
tickmode: 'auto',
zeroline: false,
autorange: true,
gridcolor: 'rgb(38.25,38.25,38.25)',
gridwidth: 1,
linecolor: 'rgb(38.25,38.25,38.25)',
linewidth: 1,
tickcolor: 'rgb(38.25,38.25,38.25)',
tickwidth: 1,
titlefont: {
size: 11,
color: 'rgb(38.25,38.25,38.25)',
family: 'Arial, sans-serif'
},
exponentformat: 'none'
},
yaxis: {
side: 'left',
type: 'linear',
range: [],
ticks: 'inside',
anchor: 'x',
domain: [0.11, 0.925],
mirror: 'ticks',
nticks: 12,
ticklen: 6.51,
showgrid: false,
showline: true,
tickfont: {
size: 10,
color: 'rgb(38.25,38.25,38.25)',
family: 'Arial, sans-serif'
},
tickmode: 'auto',
zeroline: true,
autorange: false,
gridcolor: 'rgb(38.25,38.25,38.25)',
gridwidth: 1,
linecolor: 'rgb(38.25,38.25,38.25)',
linewidth: 1,
tickcolor: 'rgb(38.25,38.25,38.25)',
tickwidth: 1,
titlefont: {
size: 11,
color: 'rgb(38.25,38.25,38.25)',
family: 'Arial, sans-serif'
},
exponentformat: 'none',
showticklabels: true
},
height: 480,
margin: {
b: 0,
l: 0,
r: 0,
t: 0,
pad: 0
},
autosize: false,
hovermode: 'closest',
titlefont: {
color: 'rgba(0,0,0,0)'
},
showlegend: false,
annotations: [
{
x: 0.5175,
y: 0.935,
font: {
size: 11,
color: 'rgb(0,0,0)',
family: 'Arial, sans-serif'
},
text: '',
xref: 'paper',
yref: 'paper',
align: 'center',
xanchor: 'center',
yanchor: 'bottom',
borderpad: 3,
showarrow: false,
textangle: 0,
bordercolor: 'rgba(0,0,0,0)',
borderwidth: 0.5
}
],
plot_bgcolor: 'rgba(0,0,0,0)',
paper_bgcolor: 'rgb(255,255,255)'
},
frames: []
}
const figureMag = {
data: [
{
line: {
dash: 'solid',
color: 'rgb(0,113.985,188.955)',
width: 1
},
mode: 'lines',
name: '',
type: 'scatter',
x: [],
y: [],
xaxis: 'x',
yaxis: 'y',
marker: {
line: {
width: 1,
color: []
},
size: 8,
symbol: 'circle',
color: []
},
visible: true,
showlegend: true
}
],
layout: {
title: '',
width: 840,
xaxis: {
title: 'Normalized Frequency (× π rad/sample)',
side: 'bottom',
type: 'linear',
range: [0, 2],
ticks: 'inside',
anchor: 'y',
domain: [0.13, 0.905],
mirror: 'ticks',
nticks: 9,
ticklen: 6.51,
showgrid: true,
showline: true,
tickfont: {
size: 10,
color: 'rgb(38.25,38.25,38.25)',
family: 'Arial, sans-serif'
},
tickmode: 'array',
tickvals: [0, 0.2, 0.4, 0.6, 0.8, 1, 1.2, 1.4, 1.6, 1.8, 2],
zeroline: false,
autorange: true,
gridcolor: 'rgba(38.25,38.25,38.25,0.25)',
gridwidth: 1,
linecolor: 'rgb(38.25,38.25,38.25)',
linewidth: 1,
tickcolor: 'rgb(38.25,38.25,38.25)',
tickwidth: 1,
titlefont: {
size: 18,
color: 'rgb(38.25,38.25,38.25)',
family: 'serif'
},
exponentformat: 'none'
},
yaxis: {
title: 'Magnitude (dB)',
side: 'left',
type: 'linear',
range: [],
ticks: 'inside',
anchor: 'x',
domain: [0.11, 0.925],
mirror: 'ticks',
nticks: 12,
ticklen: 6.51,
showgrid: true,
showline: true,
tickfont: {
size: 10,
color: 'rgb(38.25,38.25,38.25)',
family: 'Arial, sans-serif'
},
tickmode: 'auto',
zeroline: false,
autorange: true,
gridcolor: 'rgba(38.25,38.25,38.25,0.25)',
gridwidth: 1,
linecolor: 'rgb(38.25,38.25,38.25)',
linewidth: 1,
tickcolor: 'rgb(38.25,38.25,38.25)',
tickwidth: 1,
titlefont: {
size: 18,
color: 'rgb(38.25,38.25,38.25)',
family: 'serif'
},
exponentformat: 'none',
showticklabels: true
},
height: 480,
margin: {
b: 0,
l: 0,
r: 0,
t: 0,
pad: 0
},
autosize: false,
hovermode: 'closest',
titlefont: {
color: 'rgba(38.25,38.25,38.25,1)'
},
showlegend: false,
annotations: [
{
x: 0.5175,
y: 0.935,
font: {
size: 11,
color: 'rgb(0,0,0)',
family: 'Arial, sans-serif'
},
text: '',
xref: 'paper',
yref: 'paper',
align: 'center',
xanchor: 'center',
yanchor: 'bottom',
borderpad: 3,
showarrow: false,
textangle: 0,
bordercolor: 'rgba(0,0,0,0)',
borderwidth: 0.5
}
],
plot_bgcolor: 'rgba(0,0,0,0)',
paper_bgcolor: 'rgb(255,255,255)'
},
frames: []
}
const figureOut = JSON.parse(JSON.stringify(figureIn))
const figurePhase = JSON.parse(JSON.stringify(figureMag))
window.app = new Vue({
el: '#root',
data: {
figureIn: figureIn,
figureOut: figureOut,
figureMag: figureMag,
figurePhase: figurePhase,
p: 0,
q: 0,
a: [1],
b: [1],
w: [],
x: [1].concat(Array(19).fill(0)),
y: [1].concat(Array(19).fill(0)),
z: [],
preset: 'd',
length: 20,
isTrig: false,
A: 1,
T: 19,
tao: 0,
scaler: 1,
reverse: false,
toPower: false,
readonly: true,
stem: true,
systemFile: '',
fileReader: new FileReader()
},
methods: {
systemFileHandler () {
const file = document.querySelector('#systemFile').files[0]
if (file) {
this.systemFile = file
this.fileReader.readAsText(this.systemFile)
}
},
getDegreeKey (s) {
// convert 'a' -> 'p', 'b' -> 'q'
// 'p' - 'a' == 15
return String.fromCharCode(s.charCodeAt(0) + 15)
},
system (s) {
this.truncate()
const old = this[s].slice(0)
const len = this[this.getDegreeKey(s)] + 1
if (len > old.length) {
this[s] = Array(len).fill(1)
Array.prototype.splice.apply(this[s], [0, old.length].concat(old))
} else {
this[s] = old.slice(0, len)
}
this.output()
},
input (name, length) {
const omega = (2 * Math.PI) / this.T
const fn = {
d: [1].concat(Array(length - 1).fill(0)),
u: Array(length).fill(1),
sin: [...Array(length).keys()].map(
x => this.A * Math.sin(omega * (x + this.tao))
),
cos: [...Array(length).keys()].map(
x => this.A * Math.cos(omega * (x + this.tao))
),
exp: [...Array(length).keys()].map(x => Math.exp(x)),
log: [...Array(length).keys()].map(x => Math.log(x + 1)),
n: [...Array(length).keys()],
c: this.x
}
return fn[name]
},
output () {
const d = this.input('d', this.length)
const h = this.filter(this.b, this.a, d)
const x = this.input(this.preset, this.length)
this.x = x.map((x, n) => this.scale(x, n))
if (this.reverse) this.x = this.x.reverse()
this.y = this.conv(this.x, h).slice(0, this.length)
this.redrawIO(this.x, this.figureIn)
this.redrawIO(this.y, this.figureOut)
},
scale (x, n) {
const s = Math.pow(this.scaler, this.toPower ? n : 1)
return s * x
},
presetHandler () {
const s = this.preset
if (s === 'c') {
this.scaler = 1
this.toPower = false
this.readonly = false
} else {
this.length = 20
this.x = this.input(s, this.length)
this.readonly = true
if (s === 'sin' || s === 'cos') {
this.isTrig = true
} else {
this.isTrig = false
}
}
this.output()
},
lengthHandler () {
this.truncate()
if (this.length > this.x.length) {
const s = this.preset
if (s === 'c') {
const diff = this.length - this.x.length
this.x = this.x.concat(Array(diff).fill(0))
} else {
this.x = this.input(s, this.length)
}
} else {
this.x.length = this.length
}
this.output()
},
inputHandler () {
this.x = this.x.split(',')
this.length = this.x.length
this.output()
},
trigHandler () {
this.truncate()
this.output()
},
truncate () {
const keys = ['p', 'q', 'length', 'T', 'tao']
keys.forEach(key => {
this[key] = Math.trunc(this[key])
})
},
// view helper function
firstTerm (a, i) {
for (let j = 0; j < i; j += 1) {
if (a[j] !== 0) return false
}
return true
},
// view helper function
allZero (a) {
for (const i of a) {
if (i !== 0) return false
}
return true
},
filter (b, a, x) {
if (a[0] !== 0) {
const n = a.length - 1
const m = b.length - 1
const y = []
for (let i = 0; i < x.length; i += 1) {
let lhs = 0
let rhs = 0
for (let j = 1; j <= n && j <= i; j += 1) {
lhs = lhs + a[j] * y[i - j]
}
for (let j = 0; j <= m && j <= i; j += 1) {
rhs = rhs + b[j] * x[i - j]
}
y[i] = (rhs - lhs) / a[0]
}
return y
}
return x
},
conv (x, h) {
let disp = 0
const y = []
for (let k = 0; k < h.length; k += 1) {
y.push(x[0] * h[k])
}
disp = disp + 1
for (let j = 1; j < x.length; j += 1) {
for (let k = 0; k < h.length; k += 1) {
if (disp + k !== y.length) {
y[disp + k] = y[disp + k] + x[j] * h[k]
} else {
y.push(x[j] * h[k])
}
}
disp = disp + 1
}
return y
},
redrawIO (data, figure) {
const x = figure.data[0].x
const y = figure.data[0].y
const lineColor = figure.data[0].marker.line.color
const color = figure.data[0].marker.color
x.length = 0
y.length = 0
lineColor.length = 0
color.length = 0
for (let i = 0; i < this.length; i += 1) {
if (this.stem) {
figure.data[0].mode = 'lines+markers'
x.push(i, i, null)
y.push(0, data[i], null)
lineColor.push(
'rgba(0,0,0,0)',
'rgb(0,113.985,188.955)',
'rgba(0,0,0,0)'
)
color.push('rgba(0,0,0,0)', 'rgba(0,0,0,0)', 'rgba(0,0,0,0)')
} else {
figure.data[0].mode = 'lines'
x.push(i)
y.push(data[i])
}
}
Plotly.redraw(document.getElementById('input'))
Plotly.redraw(document.getElementById('output'))
},
freqz (ob, oa, oz) {
let b = ob.slice()
let a = oa.slice()
if (b.length > a.length) {
a = a.slice().concat(Array(b.length - a.length).fill(0))
} else if (b.length < a.length) {
b = b.slice().concat(Array(a.length - b.length).fill(0))
}
const H = []
for (let i = 0; i < oz.length; i += 1) {
const z = math.conj(oz[i])
let B = math.complex(0, 0)
let A = math.complex(0, 0)
for (let i = 0; i < b.length; i += 1) {
B = math.add(B, math.multiply(b[i], math.pow(z, i)))
A = math.add(A, math.multiply(a[i], math.pow(z, i)))
}
H.push(math.round(math.divide(B, A), 5))
}
return H
},
unwrap (op) {
const p = op.slice()
const dp = []
const dps = []
const dpcorr = []
const cumsum = []
const cutoff = Math.PI
for (let i = 0; i < p.length - 1; i += 1) {
dp[i] = p[i + 1] - p[i]
}
for (let i = 0; i < p.length - 1; i += 1) {
dps[i] =
dp[i] +
Math.PI -
Math.floor((dp[i] + Math.PI) / (2 * Math.PI)) * (2 * Math.PI) -
Math.PI
}
for (let i = 0; i < p.length - 1; i += 1) {
if (dps[i] === -Math.PI && dp[i] > 0) {
dps[i] = Math.PI
}
}
for (let i = 0; i < p.length - 1; i += 1) {
dpcorr[i] = dps[i] - dp[i]
}
for (let i = 0; i < p.length - 1; i += 1) {
if (Math.abs(dp[i]) < cutoff) {
dpcorr[i] = 0
}
}
cumsum[0] = dpcorr[0]
for (let i = 1; i < p.length - 1; i += 1) {
cumsum[i] = cumsum[i - 1] + dpcorr[i]
}
for (let i = 1; i < p.length; i += 1) {
p[i] += cumsum[i - 1]
}
return p
},
redrawFR () {
const mags1 = []
const mags2 = []
const phases1 = []
const phases2 = []
const H = this.freqz(this.b, this.a, this.z)
for (let i = 0; i < H.length; i += 1) {
const mag = Math.sqrt(Math.pow(H[i].re, 2) + Math.pow(H[i].im, 2))
const phase = atan2(H[i].im, H[i].re)
if (i < 512) {
mags1.push(20 * Math.log10(mag))
phases1.push(phase)
} else {
mags2.push(20 * Math.log10(mag))
phases2.push(phase)
}
}
const unwrap1 = this.unwrap(phases1).map(x => (x * 180) / Math.PI)
const unwrap2 = this.unwrap(phases2).map(x => (x * 180) / Math.PI)
mags1[511] = null
unwrap1[511] = null
mags2[0] = null
unwrap2[0] = null
figureMag.data[0].y = mags1.concat(mags2)
figurePhase.data[0].y = unwrap1.concat(unwrap2)
Plotly.redraw(document.getElementById('magnitude'))
Plotly.redraw(document.getElementById('phase'))
function atan2 (im, re) {
if (im === 0 && re === 0) {
return 0
}
return Math.atan2(im, re)
}
},
drawBuffer (width, height, context, buffer) {
context.clearRect(0, 0, width, height)
const data = buffer
const step = Math.ceil(data.length / width)
const amp = height / 2
for (let i = 0; i < width; i++) {
let min = 1.0
let max = -1.0
for (let j = 0; j < step; j++) {
const datum = data[i * step + j]
if (datum < min) min = datum
if (datum > max) max = datum
}
context.fillStyle = '#0072BD'
context.fillRect(i, (1 + min) * amp, 1, Math.max(1, (max - min) * amp))
}
},
reverb () {
const canvas = document.getElementById('waveform3')
const audioSource = new Audio('asset/sample.mp3')
const audioCtx = new AudioContext()
const audioInput = audioCtx.createMediaElementSource(audioSource)
const convolver = audioCtx.createConvolver()
const gain = audioCtx.createGain()
const analyser = audioCtx.createAnalyser()
const waveform = new Float32Array(analyser.frequencyBinCount)
let request
let chunks = new Float32Array(0)
gain.gain.value = 3.5
audioSource.onended = e => {
cancelAnimationFrame(request)
audioCtx.close()
this.drawBuffer(
canvas.width,
canvas.height,
canvas.getContext('2d'),
chunks
)
}
fetch('asset/clap.mp3')
.then(response => response.arrayBuffer())
.then(arrayBuffer => audioCtx.decodeAudioData(arrayBuffer))
.then(buffer => {
convolver.buffer = buffer
audioInput
.connect(convolver)
.connect(gain)
.connect(analyser)
.connect(audioCtx.destination)
audioSource.play()
updateWaveform()
})
function updateWaveform () {
request = requestAnimationFrame(updateWaveform)
analyser.getFloatTimeDomainData(waveform)
chunks = Float32Concat(chunks, waveform)
}
function Float32Concat (first, second) {
const firstLength = first.length
const result = new Float32Array(firstLength + second.length)
result.set(first)
result.set(second, firstLength)
return result
}
}
},
mounted () {
const input = document.getElementById('input')
const output = document.getElementById('output')
const magnitude = document.getElementById('magnitude')
const phase = document.getElementById('phase')
for (let i = 0; i <= 1023; i += 1) {
this.w.push((i * (2 * Math.PI)) / 1023)
this.z.push(math.exp(math.complex(0, this.w[i])))
}
figurePhase.layout.yaxis.title = 'Phase (degrees)'
figureMag.data[0].x = this.w.map(x => x / Math.PI)
figurePhase.data[0].x = this.w.map(x => x / Math.PI)
Plotly.plot(input, { data: figureIn.data, layout: figureIn.layout })
Plotly.plot(output, { data: figureOut.data, layout: figureOut.layout })
Plotly.plot(magnitude, { data: figureMag.data, layout: figureMag.layout })
Plotly.plot(phase, { data: figurePhase.data, layout: figurePhase.layout })
this.redrawIO(this.x, this.figureIn)
this.redrawIO(this.y, this.figureOut)
this.redrawFR()
this.fileReader.onload = e => {
const json = JSON.parse(this.fileReader.result)
this.a = json.a
this.b = json.b
this.p = this.a.length - 1
this.q = this.b.length - 1
this.output()
}
}
})

View File

@ -0,0 +1,13 @@
const path = require('path')
module.exports = {
mode: 'none',
entry: './src/main.dev.js',
output: {
path: path.resolve(__dirname, './'),
filename: 'main.js'
},
module: {
rules: [{ test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader' }]
}
}

BIN
Home/image/bg.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

47
Home/index.html Normal file
View File

@ -0,0 +1,47 @@
<!DOCTYPE html>
<html>
<head>
<title>S10659043</title>
<style>
body {
font-family: "Times New Roman", serif;
font-size: 14pt;
background: url(image/bg.jpg) no-repeat center center fixed;
background-size: cover;
}
.container {
width: 80%;
height: 20rem;
margin: 5rem auto;
padding: 0.1rem 2rem;
border-radius: 1rem;
background-color: rgba(255, 255, 255, 0.7);
}
li {
margin-top: 10px;
}
ul a {
color: rgb(55, 55, 55);
}
</style>
</head>
<body>
<div class="container">
<h1>My projects</h1>
<ul>
<li>Signals &amp; Systems</li>
<ol>
<li><a href="LTI">LTI system: Time & Freqency Response Analysis</a></li>
<li><a href="LTI/ppt">Presentation</a></li>
</ol>
<li>Concepts of Programming Languages</li>
<ol>
<li><a href="Compiler">Lexical, Syntax & Semantic Analysis</a></li>
</ol>
</ul>
</div>
</body>
</html>

2
Home/robots.txt Normal file
View File

@ -0,0 +1,2 @@
User-agent: *
Disallow: /