Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
/docs/
/example/
/js/*
!/js/browser.js
/.git/
/.github/
bsc.json
Expand All @@ -11,4 +12,4 @@ CONTRIBUTING.md
*.tgz
esbuild.config.js
*.map
/.trash/
/.trash/
40 changes: 36 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,42 @@ npm ci
npm run build
npm test
```
The build bootstraps the `.bs` CLI sources with the BynixScript compiler from the
`bynixscript` development dependency, then bundles the compiled JavaScript with
esbuild into `dist/index.min.cjs`. `npm test` also checks that every CLI source
compiles through the same compiler path.
The build pins `bynixscript@0.2.1-next` for **bootstrap only**. It compiles
`src/parser/lexer.bs`, `src/parser/syntax.bs`, and `src/parser/compiler.bs`
with that version, checks that the resulting compiler can compile its own sources,
then compiles the CLI sources with the generated compiler. esbuild packages the
CLI as `dist/index.min.cjs` and the same compiler for the browser as
`js/browser.js`. Browser and CLI do not use the old regex translator at runtime.

### Native compiler subset (v0.3.0 development)

Tokens include identifiers, decimal numbers, quoted and template strings,
operators, punctuation, newlines, and EOF; `#`, `//`, and `/* ... */` comments
are ignored. Tokens and syntax-tree nodes carry one-based line and column
positions; malformed tokens, expressions, and blocks throw `SyntaxError` with
the offending position. Blocks are terminated by `end`, not indentation;
`elif` and `else` belong to the nearest open `if`, and `recovery`/`final` to
the nearest `handle` block.

Supported statements: `func`, `if`/`elif`/`else`, `for ... of`, `while`,
`handle`/`recovery`/`final`, declarations, assignment, `return`, `throw`,
`break`, `continue`, and expression statements. Expression nodes include
literals, unary and binary operators, calls, members, indexes, arrays, objects,
grouping, and conditional expressions. Operator precedence from lowest to
highest: assignment; `||`/`??`; `&&`; equality; comparisons; `+`/`-`;
`*`/`/`/`%`; `**`; unary; call/member/index. Assignments and exponentiation
associate right-to-left. Strings remain untouched by keyword translation.

Bootstrap sources use v0.2.1-compatible `func`/`if`/`for ... of`/`end` syntax;
the build protects quoted literals from its regex passes and uses JavaScript
`while (...) { ... }` blocks where v0.2.1 lacks a `while` form. No regex pass
is used on user programs. The previous documented `match`/`case`, classes,
`delay`/`repeat`, listener/iteration callback blocks, and DOM-specific
`is_*` properties are not yet implemented and produce syntax errors instead
of falling back to regex substitution. Regular-expression literals, arrow
functions, multiline template interpolation of BynixScript syntax, and
JavaScript-style function declarations are also outside this subset. The
package remains at `0.2.2-next` until these syntax gaps are addressed.
### Using CDN <a name="using-cdn-get-started"></a>
```html
<bynix>
Expand Down
405 changes: 23 additions & 382 deletions js/browser.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"Indonesian"
],
"devDependencies": {
"bynixscript": "^0.2.1-next",
"bynixscript": "0.2.1-next",
"esbuild": "^0.25.10"
},
"repository": "https://github.com/UngGasStudio/BynixScript/"
Expand Down
27 changes: 27 additions & 0 deletions scripts/browser-entry.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const { compileSource } = require('bynix-native')

document.addEventListener('DOMContentLoaded', () => {
for (const tag of document.querySelectorAll('bynix')) {
const src = tag.getAttribute('src')
if (src) {
fetch(src)
.then(response => {
if (!response.ok) throw new Error(`Unable to fetch ${src}: ${response.status}`)
return response.text()
})
.then(source => execute(tag, source))
.catch(error => console.error(error))
} else {
execute(tag, tag.textContent)
}
}
})

function execute(tag, source) {
try {
;(0, eval)(compileSource(source))
tag.remove()
} catch (error) {
console.error(error)
}
}
33 changes: 31 additions & 2 deletions scripts/build.cjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
const fs = require('node:fs')
const path = require('node:path')
const { build } = require('esbuild')
const { compileSource } = require('./compile.cjs')
const { compileSource: bootstrapSource } = require('./compile.cjs')
const { Script } = require('node:vm')

const root = path.resolve(__dirname, '..')
const temporary = fs.mkdtempSync(path.join(root, '.bynix-build-'))
Expand All @@ -10,10 +11,28 @@ const output = path.join(root, 'dist', 'index.min.cjs')

async function main() {
try {
const parserSource = path.join(source, 'parser')
const parserOutput = path.join(temporary, 'parser')
fs.mkdirSync(parserOutput)
for (const name of ['lexer', 'syntax', 'compiler']) {
const code = bootstrapSource(fs.readFileSync(path.join(parserSource, `${name}.bs`), 'utf8'))
new Script(code, { filename: `${name}.js` })
fs.writeFileSync(path.join(parserOutput, `${name}.js`), code)
}
const nativeCompiler = require(path.join(parserOutput, 'compiler.js')).compileSource
for (const name of ['lexer', 'syntax', 'compiler']) {
new Script(nativeCompiler(fs.readFileSync(path.join(parserSource, `${name}.bs`), 'utf8')))
}
const example = 'func greet():\n print("native compiler ready")\nend\ngreet()'
const messages = []
new Script(nativeCompiler(example)).runInNewContext({ console: { log: message => messages.push(message) } })
if (messages.length !== 1 || messages[0] !== 'native compiler ready') {
throw new Error('Generated compiler failed its bootstrap smoke test')
}
for (const name of fs.readdirSync(source, { withFileTypes: true })) {
if (name.isFile() && name.name.endsWith('.bs')) {
fs.writeFileSync(path.join(temporary, name.name.replace(/\.bs$/, '.js')),
compileSource(fs.readFileSync(path.join(source, name.name), 'utf8')))
nativeCompiler(fs.readFileSync(path.join(source, name.name), 'utf8')))
}
}

Expand All @@ -24,9 +43,19 @@ async function main() {
format: 'cjs',
bundle: true,
minify: true,
banner: { js: '#!/usr/bin/env node' },
target: 'node20'
})
fs.chmodSync(output, 0o755)
await build({
entryPoints: [path.join(root, 'scripts', 'browser-entry.cjs')],
outfile: path.join(root, 'js', 'browser.js'),
platform: 'browser',
format: 'iife',
bundle: true,
minify: true,
alias: { 'bynix-native': path.join(parserOutput, 'compiler.js') }
})
} finally {
fs.rmSync(temporary, { recursive: true, force: true })
}
Expand Down
14 changes: 12 additions & 2 deletions scripts/compile.cjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
const { parsing, parsingMsg, parseCode, addSemicolons } = require('bynixscript/js/parser.js')
const replacements = require('bynixscript/dist/parser/parsingDecl.js')
const bootstrapVersion = '0.2.1-next'
if (require('bynixscript/package.json').version !== bootstrapVersion) {
throw new Error(`Bootstrap compiler must be bynixscript@${bootstrapVersion}`)
}

const passes = [
'flowReplace', 'utilityReplace', 'funcReplace', 'condReplace',
Expand All @@ -10,14 +14,20 @@ const passes = [
]

function compileSource(source) {
let code = addSemicolons(parseCode(source))
const literals = []
const protectedSource = source.replace(/(?:`(?:\\[\s\S]|[^`])*`|"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*')/g, literal => {
const placeholder = `__BYNIX_LITERAL_${literals.length}__`
literals.push(literal)
return placeholder
})
let code = addSemicolons(parseCode(protectedSource))
if (parsing(code) === false) {
throw new Error(parsingMsg.message)
}
for (const pass of passes) {
code = replacements[pass](code)
}
return code
return code.replace(/__BYNIX_LITERAL_(\d+)__/g, (_, index) => literals[Number(index)])
}

module.exports = { compileSource }
9 changes: 0 additions & 9 deletions src/parser/assignDecl.bs

This file was deleted.

9 changes: 0 additions & 9 deletions src/parser/asyncDecl.bs

This file was deleted.

14 changes: 0 additions & 14 deletions src/parser/checkDecl.bs

This file was deleted.

8 changes: 0 additions & 8 deletions src/parser/commentDecl.bs

This file was deleted.

Loading
Loading