Initial commit
This commit is contained in:
3
Home/Compiler/.babelrc
Normal file
3
Home/Compiler/.babelrc
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"presets": ["@babel/preset-env"]
|
||||
}
|
1
Home/Compiler/.npmrc
Normal file
1
Home/Compiler/.npmrc
Normal file
@ -0,0 +1 @@
|
||||
package-lock=false
|
190
Home/Compiler/.stylelintrc
Normal file
190
Home/Compiler/.stylelintrc
Normal 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
24
Home/Compiler/LICENSE
Normal 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
22
Home/Compiler/README.md
Normal 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
75
Home/Compiler/index.html
Normal 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
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
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
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
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
30
Home/Compiler/main.css
Normal 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);
|
||||
}
|
20
Home/Compiler/package.json
Normal file
20
Home/Compiler/package.json
Normal 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"
|
||||
}
|
||||
}
|
6
Home/Compiler/sandbox.html
Normal file
6
Home/Compiler/sandbox.html
Normal file
@ -0,0 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<head>
|
||||
<title>sandbox</title>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
571
Home/Compiler/src/main.dev.js
Normal file
571
Home/Compiler/src/main.dev.js
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
24
Home/Compiler/test.big5.txt
Normal file
24
Home/Compiler/test.big5.txt
Normal file
@ -0,0 +1,24 @@
|
||||
begin
|
||||
int x=0, zzz = 5;
|
||||
int i, j = 1;
|
||||
|
||||
# <20>i<EFBFBD><69><EFBFBD>j<EFBFBD><6A><EFBFBD>p<EFBFBD><70>
|
||||
for, i=1, i<=50, j; # j<>Oi<4F>C<EFBFBD><43><EFBFBD>W<EFBFBD>[<5B><><EFBFBD>ƭ<EFBFBD>
|
||||
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
|
24
Home/Compiler/test.utf8.txt
Normal file
24
Home/Compiler/test.utf8.txt
Normal 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
|
13
Home/Compiler/webpack.config.js
Normal file
13
Home/Compiler/webpack.config.js
Normal 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' }]
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user