Skip to content

Commit

Permalink
Compiler config cleanup
Browse files Browse the repository at this point in the history
- eliminated hack checking for test fixture in file path
- Can pass `isLightScript: true` to Babel opts to force compile as LSC
- Updated test suite
  • Loading branch information
wcjohnson committed Oct 7, 2017
1 parent 416c326 commit 3936ca6
Show file tree
Hide file tree
Showing 20 changed files with 168 additions and 171 deletions.
178 changes: 104 additions & 74 deletions src/config.lsc
Original file line number Diff line number Diff line change
Expand Up @@ -2,78 +2,91 @@ import * as babylonLightScript from "@oigroup/babylon-lightscript";
import packageMetadata from "../package.json";
import parseConfigurationDirectives from './util/parseConfigurationDirectives'

export getMetadata() ->
{
parser: babylonLightScript
getParserOpts
parseConfigurationDirectives
name: packageMetadata.name
version: packageMetadata.version
options: {
stdlib: {
description: "Configure the LightScript standard library"
}
patternMatching: {
description: "Test and branch using `match` keyword"
valueType: "option"
options: ["default", "enhanced"]
defaultValue: "default"
stage: "1"
}
whiteblock: {
description: "Disable `{` `}` as block delimiters."
valueType: "boolean"
}
bangCall: {
description: "Call functions with paren-free syntax using `!`"
valueType: "boolean"
defaultValue: true
}
safeCall: {
description: "Call a function using `?` to check callability first"
valueType: "boolean"
defaultValue: true
}
existential: {
description: "Postfix `?` checks that an expression is not loose-equal to `null`"
valueType: "boolean"
}
enhancedComprehension: {
description: "Positional comprehensions and `case` syntax for comprehensions"
valueType: "boolean"
defaultValue: true
stage: "0"
}
noEnforcedSubscriptIndentation: {
description: "Do not enforce indentation for subscripts on subsequent lines."
valueType: "boolean"
}
useRequire: {
description: "Generate `require` rather than `import` when the compiler introduces a module."
valueType: "boolean"
}
flippedImports: {
description: "Allow imports via `import 'path': [specifier]` syntax."
valueType: "boolean"
}
disableJsx: {
description: "Don't parse JSX expressions."
valueType: "boolean"
}
disableFlow: {
description: "Don't parse Flow type annotations."
valueType: "boolean"
}
placeholderArgs: {
description: "Specify arguments for a function using placeholders in the body."
valueType: "boolean"
}
placeholder: {
description: "Specify an identifier to be transformed into PlaceholderExpression."
valueType: "string"
}
export getMetadata() -> {
parser: babylonLightScript
getParserOpts
parseConfigurationDirectives
name: packageMetadata.name
version: packageMetadata.version
options: {
stdlib: {
description: "Configure the LightScript standard library"
}
patternMatching: {
description: "Test and branch using `match` keyword"
valueType: "option"
options: ["default", "enhanced"]
defaultValue: "default"
stage: "1"
}
whiteblock: {
description: "Disable `{` `}` as block delimiters."
valueType: "boolean"
}
bangCall: {
description: "Call functions with paren-free syntax using `!`"
valueType: "boolean"
defaultValue: true
}
safeCall: {
description: "Call a function using `?` to check callability first"
valueType: "boolean"
defaultValue: true
}
existential: {
description: "Postfix `?` checks that an expression is not loose-equal to `null`"
valueType: "boolean"
}
enhancedComprehension: {
description: "Positional comprehensions and `case` syntax for comprehensions"
valueType: "boolean"
defaultValue: true
stage: "0"
}
noEnforcedSubscriptIndentation: {
description: "Do not enforce indentation for subscripts on subsequent lines."
valueType: "boolean"
}
useRequire: {
description: "Generate `require` rather than `import` when the compiler introduces a module."
valueType: "boolean"
}
flippedImports: {
description: "Allow imports via `import 'path': [specifier]` syntax."
valueType: "boolean"
}
disableJsx: {
description: "Don't parse JSX expressions."
valueType: "boolean"
}
disableFlow: {
description: "Don't parse Flow type annotations."
valueType: "boolean"
}
placeholderArgs: {
description: "Specify arguments for a function using placeholders in the body."
valueType: "boolean"
}
placeholder: {
description: "Specify an identifier to be transformed into PlaceholderExpression."
valueType: "string"
}
}
}

getFileTypeInfo(file) ->
if not file?.opts?.filename: return { isLightScript: true, isLSX: false }
filename = file.opts.filename

if filename.includes(".lsx"): return { isLightScript: true, isLSX: true }

if (
filename === "unknown" or // eslint
filename === "repl" or // babel-node
filename.includes(".lsc")
): return { isLightScript: true, isLSX: false }

return { isLightScript: false, isLSX: false }

export getParserOpts(pluginOpts, initialParserOpts) ->
parserOpts = initialParserOpts or {}
Expand Down Expand Up @@ -116,12 +129,29 @@ export getParserOpts(pluginOpts, initialParserOpts) ->

parserOpts

export preParseInitialization(compiler, babelFile, pluginOpts) ->
// Pre-parse initialization of compiler state. All configuration data is
// collected here.
export preParseInitialization(compiler, babelFile, pluginOpts): void ->
compiler.file = babelFile
compiler.opts = pluginOpts

babelFile.parseShebang()
{ opts, parserOpts, code } = babelFile
directiveOpts = parseConfigurationDirectives(code)
fileTypeInfo = getFileTypeInfo(babelFile)
if fileTypeInfo.isLightScript and (pluginOpts.isLightScript == undefined):
pluginOpts.isLightScript = true

directiveOpts = parseConfigurationDirectives(code)
Object.assign(pluginOpts, directiveOpts)

if pluginOpts.isLightScript:
compiler.isLightScript = true
babelFile.isLightScript = true
compiler.isLSX = pluginOpts.isLSX
babelFile.isLSX = pluginOpts.isLSX

// XXX: shouldn't be needed, refactor
export { parseConfigurationDirectives }
// Set up LSC parsing pipeline
opts.parserOpts = opts.parserOpts or {}
nextParserOpts = getParserOpts(pluginOpts, opts.parserOpts)
parserOpts.parse = nextParserOpts.parse
parserOpts.plugins = nextParserOpts.plugins
62 changes: 11 additions & 51 deletions src/index.lsc
Original file line number Diff line number Diff line change
@@ -1,43 +1,13 @@
import patch from './util/babelTypesPatch'
import { registerLightscriptNodeTypes } from './lscNodeTypes'

import { resetHelpers } from "./state/helpers"; // XXX: should be part of state init
import { getParserOpts, parseConfigurationDirectives } from './config'
import { preParseInitialization } from './config'
import findBabelOptionsForPlugin from './util/findBabelOptionsForPlugin'
import { getFileTypeInfo, createCompilerState, initializeCompilerState, postprocess } from './state/compilerState'
import { createCompilerState, preCompileInitialization, postprocess } from './state/compilerState'

import visitAstFixup from './visitors/fixAst'
import visitPlaceholders from './visitors/placeholders'
import visitOptionalChains from './visitors/optionalChains'
import visitMain from './visitors/main'

// TODO: factor out to config.lsc
computeParserOpts(file, pluginOpts) ->
// By this time, we have access to the code of the file.
// We can parse shebang, directives, etc and manipulate parser options
// based on those data.
file.parseShebang()
{ opts, parserOpts } = file
fileTypeInfo = getFileTypeInfo(file)
cdOpts = parseConfigurationDirectives(file.code)

// Don't read as LSC
if (not fileTypeInfo.isLightScript) and (not cdOpts.isLightScript): return

file.isLightScript = true
Object.assign(pluginOpts, cdOpts)

// Get LSC parser opts
opts.parserOpts = opts.parserOpts or {}
nextParserOpts = getParserOpts(pluginOpts, opts.parserOpts)
parserOpts.parse = nextParserOpts.parse
parserOpts.plugins = nextParserOpts.plugins

export default LightScript(babel) ->
{ types: t } = babel
patch(t)
registerLightscriptNodeTypes(t)

plugin = {
manipulateOptions(opts, parserOpts, file) ->
// manipulateOptions is called very early in the Babel processing chain.
Expand All @@ -60,32 +30,22 @@ export default LightScript(babel) ->
origAddCode.call(this, code)
pluginOpts = findBabelOptionsForPlugin(this.opts, plugin)

// compiler = createCompilerState()
// this.lscCompiler = compiler
// compiler~preParseInitialization(this, code, this.opts, pluginOpts)
computeParserOpts(this, pluginOpts)
compiler = createCompilerState(babel)
this.lscCompiler = compiler
compiler~preParseInitialization(this, pluginOpts)
return

visitor: {
// The compiler's main loop is a Program visitor.
Program(path, state): void ->
////////// Initialize compilation run
// Obtain configuration
opts = state.opts
compiler = createCompilerState({
programPath: path
babel
opts
file: state.file
})
// Skip non-LightScript code
if not state.file.isLightScript: return

// Early-out if not a LightScript program
if (!compiler.isLightScript) return
// Obtain compiler state created during early processing
compiler = state.file.lscCompiler

initializeCompilerState()

// TODO: helpers -> compilerState
resetHelpers(path)
////////// Initialize compilation run
compiler~preCompileInitialization(path)

////////// AST visitation and transformation
// First pass: Perform basic ast fixups (block bodies, etc)
Expand Down
61 changes: 23 additions & 38 deletions src/state/compilerState.lsc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { registerLightscriptNodeTypes } from '../lscNodeTypes'
import { initializeStdlib } from './stdlib'
import { initializeRuntime } from './runtime'
import { insertImports } from './imports'
import { resetHelpers } from './helpers'
import { getInlinedOperatorsEnabled } from '../transforms/inlinedOperators' // XXX

import { getLoc, span } from 'ast-loc-utils'
Expand Down Expand Up @@ -32,9 +33,9 @@ let compilerState = {
// lightscript-runtime state
runtime: null

inlinedOperatorsEnabled: true
isLightScript: true
isLSX: false
inlinedOperatorsEnabled: null
isLightScript: null
isLSX: null
}

export getCompilerState() -> compilerState
Expand All @@ -43,24 +44,6 @@ export getBabel() -> compilerState.babel

export getOptions() -> compilerState.opts

export getFileTypeInfo(file) ->
if not file?.opts?.filename: return { isLightScript: true, isLSX: false }
filename = file.opts.filename

if filename.includes(".lsx"): return { isLightScript: true, isLSX: true }

if (
filename === "unknown" or // eslint
filename === "repl" or // babel-node
filename.includes(".lsc") or
filename.includes("test/fixtures")
): return { isLightScript: true, isLSX: false }

return { isLightScript: false, isLSX: false }

processFilename(state): void ->
Object.assign(state, getFileTypeInfo(state.file))

// This will get the position of the first non-comment token. We place imports
// and requires here because ESLint requires the corresponding string literals
// to be on top of a valid token, or it chokes.
Expand All @@ -72,37 +55,39 @@ findFirstTokenLoc(tokens) ->

return null

export createCompilerState(initialState) ->
now compilerState = initialState
processFilename(compilerState)
// Allow config directives to override filename processing
if initialState.file?.isLightScript: compilerState.isLightScript = true
export createCompilerState(babel) ->
// TODO: Eliminate global here. Compiler should pass state down using
// `~` where necessary.
now compilerState = { babel }
compilerState

export preCompileInitialization(compiler, programPath) ->
{ babel, opts, file } = compiler
compiler.programPath = programPath

patch(babel.types)
registerLightscriptNodeTypes(babel.types)

// Get zero-location
tokens = initialState?.file?.ast?.tokens
body = initialState?.programPath?.node?.body
tokens = file?.ast?.tokens
body = programPath?.node?.body
loc = findFirstTokenLoc(tokens)
if loc:
initialState.firstLoc = loc
compiler.firstLoc = loc
elif body and body.length > 0:
initialState.firstLoc = getLoc(body[0])~span(1)
compiler.firstLoc = getLoc(body[0])~span(1)
else:
initialState.firstLoc = {
compiler.firstLoc = {
start: 0
end: 1
loc: { start: { line: 1, column: 0 }, end: { line: 1, column: 1 } }
}

return compilerState

export initializeCompilerState() ->
{ babel, opts } = compilerState
patch(babel.types)
registerLightscriptNodeTypes(babel.types)

// XXX: fix this
compilerState.stdlib = initializeStdlib(opts)
compilerState.inlinedOperatorsEnabled = getInlinedOperatorsEnabled(opts)
compilerState.runtime = initializeRuntime(opts)
resetHelpers(programPath)

compilerState

Expand Down
1 change: 1 addition & 0 deletions test/fixtures/bang-calls/options.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"plugins": [
["lightscript", {
"isLightScript": true,
"bangCall": true,
"safeCall": true
}]
Expand Down
2 changes: 1 addition & 1 deletion test/fixtures/comprehensions/options.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"plugins": [["lightscript", { "enhancedComprehension": false }]]
"plugins": [["lightscript", { "isLightScript": true, "enhancedComprehension": false }]]
}
Loading

0 comments on commit 3936ca6

Please sign in to comment.