diff --git a/index.html b/index.html
index 826dc88..8b960bc 100644
--- a/index.html
+++ b/index.html
@@ -107,7 +107,9 @@
RamdaScript
// Output JS code
(alter outputJs (fn []
- (alter output.value (RamdaScript.compile source.value))))
+ (alter output.value
+ (prop 'js'
+ (RamdaScript.compile source.value)))))
// Select snippet
(def setSource (fn [src]
diff --git a/scripts/test.js b/scripts/test.js
index d462f80..241457b 100644
--- a/scripts/test.js
+++ b/scripts/test.js
@@ -6,7 +6,7 @@ global.T = require('../src/nodes').type
global.R = require('../vendor/ramda')
global.run = function run(src, vars) {
- var js = ram.compile(src, '')
+ var js = ram.compile(src, {filename: ''}).js
var fn = new Function('vars', js)
return fn.call(null, vars)
}
diff --git a/src/browser.js b/src/browser.js
index 47d3f08..64db220 100644
--- a/src/browser.js
+++ b/src/browser.js
@@ -2,8 +2,8 @@ window.RamdaScript = module.exports = require('./ramdascript')
window.RamdaScript.run = run
function run(src) {
- var js = RamdaScript.compile(src, {wrapper: 'none'})
- var fn = new Function(js)
+ var result = RamdaScript.compile(src, {format: 'none'})
+ var fn = new Function(result.js)
return fn()
}
diff --git a/src/chunk.js b/src/chunk.js
new file mode 100644
index 0000000..c4d31d1
--- /dev/null
+++ b/src/chunk.js
@@ -0,0 +1,17 @@
+
+module.exports = function chunk(content, loc) {
+ var chunk = {
+ content : content || '',
+ loc : loc
+ }
+
+ chunk.toString = chunkToString(chunk)
+
+ return chunk
+}
+
+function chunkToString(chunk) {
+ return function () {
+ return chunk.content
+ }
+}
\ No newline at end of file
diff --git a/src/cli.js b/src/cli.js
index 4eab456..645ce28 100644
--- a/src/cli.js
+++ b/src/cli.js
@@ -21,15 +21,15 @@ var help = ['',
'Run .ram file containig RamdaScript code',
' -src Route to source file with RamdaScript code',
'',
-'compile [-src] [-dst] [-nowrap]',
+'compile [-src] [-dst] [-format]',
'',
'Compile to JavaScript and save as .js file',
' -src Route to source file or directory with RamdaScript code',
' the default value is the current directory (cwd)',
' -dst Route where the resulting JavaScript code will be saved',
' if route is "stdout" the resulting code will be sent to stdout',
-' -wrap Specify module wrapper, possible values are: commonjs, closure, none.',
-' commonjs is the default',
+' -format Specify module format, possible values are: cjs, iife, none.',
+' cjs is the default',
'',
'eval [-src]',
'',
@@ -92,12 +92,12 @@ function run(opts) {
// register `.ram` extension in nodejs modules
Module._extensions[util.ext] = function(module, filename) {
var content = fs.readFileSync(filename, 'utf8')
- var js = ram.compile(content, {
+ var result = ram.compile(content, {
filename: filename,
- // just use commonjs export `R` will be imported from global
- wrapper : 'commonjs-export',
+ // just use commonjs export, `R` will be imported from global
+ format : 'cjs-export',
})
- module._compile(js, filename)
+ module._compile(result.js, filename)
}
var src = opts['src']
@@ -121,7 +121,7 @@ function _eval(opts) {
}
function run(src) {
- var fn = new Function('R', ram.compile(src, ''))
+ var fn = new Function('R', ram.compile(src, '').js)
fn(R)
}
}
@@ -130,7 +130,7 @@ function _eval(opts) {
function compile(opts) {
var srcPath = opts['src'] || process.cwd()
var dstPath = opts['dst']
- var wrapper = opts['wrap'] || 'commonjs'
+ var format = opts['format'] || 'cjs'
var toStdout = dstPath === 'stdout'
var stat = fs.statSync(srcPath)
@@ -147,9 +147,9 @@ function compile(opts) {
}
if (stat.isFile()) {
- compileFile(srcPath, dstPath, wrapper, toStdout)
+ compileFile(srcPath, dstPath, format, toStdout)
} else if (stat.isDirectory()) {
- compileDir(srcPath, dstPath, wrapper)
+ compileDir(srcPath, dstPath, format)
}
}
@@ -157,14 +157,14 @@ function compile(opts) {
// files and saves the resulting JavaScript code to the provided destiny
// if the destiny path is omitted the JavaScript code will be saved adjacent
// to the RamdaScript source file
-function compileDir(srcPath, dstPath, wrapper, toStdout) {
- fs.readdir(srcPath, function(err, list){
+function compileDir(srcPath, dstPath, format, toStdout) {
+ fs.readdir(srcPath, function(err, list) {
if (err) throw err
- list.forEach(function(fname){
+ list.forEach(function(fname) {
fname = path.join(srcPath, fname)
- fs.stat(fname, function(err, stat){
+ fs.stat(fname, function(err, stat) {
if (err) throw err
// skip files and dirs with name starting with `.`
@@ -173,9 +173,9 @@ function compileDir(srcPath, dstPath, wrapper, toStdout) {
}
if (stat.isFile() && path.extname(fname) === util.ext) {
- compileFile(fname, path.join(dstPath, path.basename(fname, util.ext) + '.js'), wrapper, toStdout)
+ compileFile(fname, path.join(dstPath, path.basename(fname, util.ext) + '.js'), format, toStdout)
} else if (stat.isDirectory()) {
- compileDir(fname, path.join(dstPath, path.basename(fname)), wrapper, toStdout)
+ compileDir(fname, path.join(dstPath, path.basename(fname)), format, toStdout)
}
})
})
@@ -185,39 +185,54 @@ function compileDir(srcPath, dstPath, wrapper, toStdout) {
// compile a the provided file containig RamdaScript code and save the
// resulting JavaScript code, if the destiny path is omitted the JavaScript code
// will be saved adjacent to the RamdaScript source file
-function compileFile(srcPath, dstPath, wrapper, toStdout) {
+function compileFile(srcPath, dstPath, format, toStdout) {
if (! fs.existsSync(dstPath)) {
makePath(dstPath)
}
fs.readFile(srcPath, 'utf8', function(err, data) {
- var js
-
if (err) throw err
- js = ram.compile(data, {
- filename: srcPath,
- wrapper : wrapper
+ var cwd = process.cwd()
+ var smapDstPath = dstPath + '.map'
+ var smapSrcPath = path.relative(
+ path.dirname(
+ path.join(cwd, dstPath)),
+ path.join(cwd, srcPath))
+ var result = ram.compile(data, {
+ filename : srcPath,
+ format : format,
+ sourceMap: !toStdout,
+ sourceMapCfg: {
+ source: smapSrcPath,
+ sourceContent: data
+ }
})
if (toStdout) {
- process.stdout.write(js + '\n')
+ process.stdout.write(result.js + '\n')
return
}
- if (js) {
- fs.writeFile(dstPath, js, 'utf8', function(err){
+ if (result.js) {
+ result.js = result.js + '\n//# sourceMappingURL=' + path.basename(dstPath) + '.map'
+ fs.writeFile(dstPath, result.js, 'utf8', function(err) {
+ if (err) throw err
+ })
+ }
+
+ if (result.sourceMap) {
+ fs.writeFile(smapDstPath, result.sourceMap, 'utf8', function(err) {
if (err) throw err
})
}
})
}
-// makes a directory path in the filesysytem
-
+// makes a directory path in the filesystem
function makePath(dirPath) {
dirPath = path.dirname(dirPath).split(path.sep)
- dirPath.reduce(function(dirPath, p){
+ dirPath.reduce(function(dirPath, p) {
dirPath = path.join(dirPath, p)
if (! fs.existsSync(dirPath)) {
try {
diff --git a/src/compiler.js b/src/compiler.js
index 502a768..35c1c3c 100644
--- a/src/compiler.js
+++ b/src/compiler.js
@@ -1,8 +1,8 @@
-var nodes = require('./nodes')
-var util = require('./util')
-var T = nodes.type
-var newNode = nodes.node
+var nodes = require('./nodes')
+var util = require('./util')
+var T = nodes.type
+var newNode = nodes.node
// walk through nodes
function walk(node, parent, ctx) {
@@ -38,7 +38,7 @@ function walk(node, parent, ctx) {
visitObject(node, parent, ctx)
break
case T.JSBLOCK :
- visitJsBlock(node, parent, ctx)
+ visitJSBlock(node, parent, ctx)
break
case T.NIL :
visitNil(node, parent, ctx)
@@ -58,17 +58,17 @@ function walk(node, parent, ctx) {
}
function visitLiteral(node, parent, ctx) {
- ctx.write(node.content)
+ ctx.write(node.content, node.loc)
}
function visitNil(node, parent, ctx) {
- ctx.write('null')
+ ctx.write('null', node.loc)
}
function visitString(node, parent, ctx) {
var str = node.content
node.content = str.replace(/\n|\r\n/g, '\\\n')
- ctx.write(node.content)
+ ctx.write(node.content, node.loc)
}
function visitIdent(node, parent, ctx) {
@@ -91,12 +91,12 @@ function visitIdent(node, parent, ctx) {
cont = 'R.' + cont
}
}
- ctx.write(cont)
+ ctx.write(cont, node.loc)
}
function visitSpecialPlaceholder(node, parent, ctx) {
ctx.addUsedRamdaFn('__')
- ctx.write('R.__')
+ ctx.write('R.__', node.loc)
}
function visitModule(node, parent, ctx) {
@@ -121,13 +121,13 @@ function visitSexpr(node, parent, ctx) {
case 'new' :
operator = data[0]
data = data.slice(1)
- ctx.write('new ')
+ ctx.write('new ', node.loc)
break
case 'def' :
var varName = data[0]
var value = data[1]
- ctx.write('var ')
+ ctx.write('var ', node.loc)
walk(varName, node, ctx)
ctx.write(' = ')
if (value) {
@@ -142,10 +142,10 @@ function visitSexpr(node, parent, ctx) {
// curry function if args number is greater than 0
if (args.length === 0) {
- ctx.write('function (')
+ ctx.write('function (', node.loc)
} else {
ctx.addUsedRamdaFn('curry')
- ctx.write('R.curry(function (')
+ ctx.write('R.curry(function (', node.loc)
}
// write arguments
@@ -226,7 +226,7 @@ function visitSexpr(node, parent, ctx) {
// write the name the imported module
// will be recognized with
if (refer.type == T.IDENT) {
- ctx.write('var ')
+ ctx.write('var ', node.loc)
walk(refer, node, ctx)
ctx.write(' = require(')
walk(modPath, node, ctx)
@@ -327,7 +327,7 @@ function visitObject(node, parent, ctx) {
ctx.write('}')
}
-function visitJsBlock(node, parent, ctx) {
+function visitJSBlock(node, parent, ctx) {
var str = node.content
node.content = util.trim(str.substring(2, str.length - 2))
visitLiteral(node, parent, ctx)
@@ -338,17 +338,9 @@ function isIndentableNode(node) {
return node.type === T.SEXPR
}
-// write CommonJS wrapper
-function writeCommonJSWrapper(requireRamda, ctx) {
- /*
- if (Object.keys(ctx.usedRamdaFns).length > 0) {
- ctx.writeTop('var R = require(\'ramda\')\n\n')
- }
- */
-
- /*
- Granular require for each Ramda function, for minimal bundle size
- */
+// write CommonJS stub
+function writeCommonJSStub(ast, ctx, requireRamda) {
+ // Granular require for each Ramda function, for minimal bundle size
if (requireRamda) {
var usedFns = []
@@ -357,7 +349,21 @@ function writeCommonJSWrapper(requireRamda, ctx) {
})
if (usedFns.length > 0) {
- ctx.writeTop('var R = {\n' + usedFns.join(',\n') + '\n}\n\n')
+ ctx.newLineTop()
+ ctx.newLineTop()
+ ctx.writeTop('}')
+ ctx.newLineTop()
+
+ usedFns.forEach(function(fnName, idx) {
+ if (idx != 0) {
+ ctx.newLineTop()
+ ctx.writeTop(',')
+ }
+ ctx.writeTop(fnName)
+ })
+
+ ctx.newLineTop()
+ ctx.writeTop('var R = {')
}
}
@@ -372,46 +378,49 @@ function writeCommonJSWrapper(requireRamda, ctx) {
})
}
-// write Closure wrapper
-function writeClosureWrapper(ctx) {
- ctx.writeTop(';(function () {\n\n')
+// write IIFE stub
+function writeIIFEStub(ctx) {
+ ctx.newLineTop()
+ ctx.newLineTop()
+ ctx.writeTop(';(function () {')
+
ctx.newLine()
ctx.newLine()
ctx.write('})()')
}
-function writeWaterMark(ctx) {
+function writeCompilerInfo(ctx) {
var version = require('../package.json').version
- ctx.writeTop('// Generated by RamdaScript ' + version + '\n\n')
+ ctx.newLineTop()
+ ctx.newLineTop()
+ ctx.writeTop('// Generated by RamdaScript ' + version)
}
// convert the AST in JS code
// params
-// ast AST
-// ctx shared context
-// wrapper module wrapper (commonjs, closure, none)
+// ast AST
+// ctx Shared context
+// format Module format (cjs, iife, none)
//
-exports.compileAst = function compileAst(ast, ctx, wrapper) {
+exports.astToChunks = function astToChunks(ast, ctx, format) {
walk(ast, null, ctx)
+ format = format || 'none'
- wrapper = wrapper || 'none'
-
- switch (wrapper) {
- case 'commonjs' :
- writeCommonJSWrapper(true, ctx)
+ switch (format) {
+ case 'cjs' :
+ writeCommonJSStub(ast, ctx, true)
break
- case 'commonjs-export' :
- writeCommonJSWrapper(false, ctx)
+ case 'cjs-export' :
+ writeCommonJSStub(ast, ctx, false)
break
- case 'closure' :
- writeClosureWrapper(ctx)
+ case 'iife' :
+ writeIIFEStub(ctx)
break
case 'none' :
break
default :
- throw '`' + wrapper + '` is not a valid wrapper'
+ throw '`' + format + '` is not a valid format'
}
-
- writeWaterMark(ctx)
- return ctx.out()
+ writeCompilerInfo(ctx)
+ return ctx.chunks
}
\ No newline at end of file
diff --git a/src/context.js b/src/context.js
index 07e3431..d1c1dfa 100644
--- a/src/context.js
+++ b/src/context.js
@@ -1,4 +1,6 @@
+var chunk = require('./chunk')
+
// Returns a new compilation context object to be shared
// between compilation steps (semantic analizing and compilation)
// it keeps track of defined variables, used ramda functions,
@@ -10,7 +12,7 @@ exports.newContext = function newContext(filename) {
filename: filename || '',
// the compiled JS
- buffer: [],
+ chunks: [],
indentLevel: 0,
@@ -29,22 +31,35 @@ exports.newContext = function newContext(filename) {
// returns the compiled JS
out: function out() {
- return this.buffer.join('')
+ return this.chunks.join('')
},
- // write to the buffer
- write: function(d) {
- this.buffer.push(d)
+ // write a new chunk
+ write: function(d, loc) {
+ if (d.indexOf('\n') != -1) {
+ throw 'Can not write content having \\n'
+ }
+ this.chunks.push(chunk(d, loc))
},
- // write at top of the buffer
- writeTop: function(d) {
- this.buffer.unshift(d)
+ // unshift a new chunk
+ writeTop: function writeTop(d, loc) {
+ if (d.indexOf('\n') != -1) {
+ throw 'Can not write content having \\n'
+ }
+ this.chunks.unshift(chunk(d, loc))
},
// write a new line and indentation
newLine: function newLine() {
- this.buffer.push('\n' + this.currentIndent)
+ this.chunks.push(chunk('\n'))
+ this.chunks.push(chunk(this.currentIndent))
+ },
+
+ // write a new line and indentation
+ newLineTop: function newLineTop() {
+ this.chunks.unshift(chunk(this.currentIndent))
+ this.chunks.unshift(chunk('\n'))
},
// updates current indentation spaces depending
diff --git a/src/ramdascript.js b/src/ramdascript.js
index 1a5237b..62c6060 100644
--- a/src/ramdascript.js
+++ b/src/ramdascript.js
@@ -1,40 +1,94 @@
-var Parser = require('./parser').Parser
-var context = require('./context')
-var compiler = require('./compiler')
-var semantic = require('./semantic')
-var util = require('./util')
+var Parser = require('./parser').Parser
+var context = require('./context')
+var compiler = require('./compiler')
+var semantic = require('./semantic')
+var sourcemap = require('./sourcemap')
+var util = require('./util')
// Compiles RamdaScript source code to JS, if returnCtx is true it
// return the context intead of compiled JS
// otps:
-// filename The name of the source file
-// wrapper module wrapper (commonjs, closure, none)
+// filename The name of the source file
+// format Module format (cjs, iife, none)
+// printErrors Whether to print errors to console
+// sourceMap Whether to build sourcemaps
+// sourceMapCfg Sourcemap configuration object
//
-exports.compile = function compile(src, opts, returnCtx) {
- opts = opts || {}
- var ctx = context.newContext(opts.filename)
- var ast = parse(src, opts)
+exports.compile = function compile(src, opts) {
+ opts = opts || {}
+ var js = ''
+ var smap = ''
+ var ctx = context.newContext(opts.filename)
+ var chunks = srcToChunks(src, opts, ctx)
+ if (chunks) {
+ js = chunks.join('')
+ if (opts.sourceMap) {
+ smap = chunksToSourceMap(chunks, opts.sourceMapCfg)
+ }
+ }
+ return {
+ js : js,
+ ctx : ctx,
+ sourceMap: smap
+ }
+}
+// transform RamdaScript source to chunks
+function srcToChunks(src, opts, ctx) {
+ opts = opts || {}
+ var ast = parse(src, opts)
// check semantic
var errors = semantic.checkAst(ast, ctx)
-
- if (returnCtx) {
- return ctx
- }
-
// there is semantic errors?
if (errors) {
- errors.forEach(function(e){
- console.error(e)
- })
+ if (opts.printErrors !== false) {
+ errors.forEach(function(e){
+ console.error(e)
+ })
+ }
return
// everything ok
} else {
- return compiler.compileAst(ast, ctx, opts.wrapper)
+ return compiler.astToChunks(ast, ctx, opts.format)
}
}
+// convert chunks to source map
+// chunks array of chunks
+// cfg sourcemap config
+//
+function chunksToSourceMap(chunks, cfg) {
+ var loc
+ var outLine = 0
+ var outColumn = 0
+ var smap = sourcemap.newSourceMap()
+ var acc = ''
+
+ chunks.forEach(function(chunk) {
+ if (chunk.content === '\n') {
+ outLine = outLine + 1
+ outColumn = 0
+ }
+ loc = chunk.loc
+ if (loc) {
+ smap.add(
+ // jison line tracking is 1 based,
+ // source maps reads it as 0 based
+ loc.firstLine - 1,
+ loc.firstColumn,
+ outLine,
+ outColumn
+ )
+ }
+ if (chunk.content !== '\n') {
+ outColumn = outColumn + chunk.content.length
+ }
+ })
+
+ return smap.generate(cfg)
+}
+
// parses the RamdaScript code
function parse(src, opts) {
var parser = new Parser()
@@ -42,4 +96,6 @@ function parse(src, opts) {
return parser.parse(src)
}
+exports.srcToChunks = srcToChunks
+exports.chunksToSourceMap = chunksToSourceMap
exports.parse = parse
\ No newline at end of file
diff --git a/src/repl.js b/src/repl.js
index 0f2772c..50116d7 100644
--- a/src/repl.js
+++ b/src/repl.js
@@ -15,14 +15,16 @@ exports.launch = function launch(extCtx) {
repl.start('ram> ', null, _eval)
function _eval(code, ctx, file, cb) {
- var js, err, result
+ var js
+ var err
+ var result
try {
// import passed context
Object.assign(ctx, extCtx)
- js = ram.compile(code, {
+ result = ram.compile(code, {
filename: ''
})
- result = vm.runInContext(js, ctx, file)
+ result = vm.runInContext(result.js, ctx, file)
} catch (e) {
err = e
}
diff --git a/src/sourcemap.js b/src/sourcemap.js
new file mode 100644
index 0000000..5bd976e
--- /dev/null
+++ b/src/sourcemap.js
@@ -0,0 +1,151 @@
+
+// This is a version,
+// the original code can be found at
+// https://github.com/jashkenas/coffeescript/tree/master/src/sourcemap.litcoffee
+
+var VLQ_SHIFT = 5
+var VLQ_CONTINUATION_BIT = 1 << VLQ_SHIFT
+var VLQ_VALUE_MASK = VLQ_CONTINUATION_BIT - 1
+var BASE64_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
+
+function toVlq(value) {
+ var nextChunk
+ var vlq = ''
+ var signBit = value < 0 ? 1 : 0
+ var valueToEncode = (Math.abs(value) << 1) + signBit
+
+ while (valueToEncode || !vlq) {
+ nextChunk = valueToEncode & VLQ_VALUE_MASK
+ valueToEncode = valueToEncode >> VLQ_SHIFT
+
+ if (valueToEncode) {
+ nextChunk |= VLQ_CONTINUATION_BIT
+ }
+
+ vlq += toBase64(nextChunk)
+ }
+ return vlq
+}
+
+function toBase64(value) {
+ var b64 = BASE64_CHARS[value]
+
+ if (!b64) {
+ throw 'Can not encode ' + value + ' to base-64'
+ }
+ return b64
+}
+
+var newLineMap = function newLineMap(l) {
+ return {
+ line : l,
+ segments: [],
+
+ add: function(generatedColumn, sourceLine, sourceColumn) {
+ this.segments[generatedColumn] = {
+ line : this.line,
+ column : generatedColumn,
+ sourceLine : sourceLine,
+ sourceColumn: sourceColumn
+ }
+
+ return this.segments
+ }
+ }
+}
+
+exports.newSourceMap = function newSourceMap() {
+ return {
+ lines: [],
+ names: null,
+
+ add: function(sourceLine, sourceColumn, generatedLine, generatedColumn) {
+ var line = this.lines[generatedLine]
+
+ if (!line) {
+ line = this.lines[generatedLine] = newLineMap(generatedLine)
+ }
+
+ line.add(generatedColumn, sourceLine, sourceColumn)
+ },
+
+ generate: function(cfg) {
+ cfg = cfg || {}
+
+ var i
+ var j
+ var line
+ var sm
+ var segment
+ var segmentsLen
+ var currentLine = 0
+ var lastSourceLine = 0
+ var lastSourceColumn = 0
+ var lastColumn = 0
+ var linesLen = this.lines.length
+ var mapping = ''
+ var segmentSep = ''
+
+ for (i = 0; i < linesLen; i++) {
+ line = this.lines[i]
+
+ if (!line) continue
+
+ segmentsLen = line.segments.length
+
+ for (j = 0; j < segmentsLen; j++) {
+ segment = line.segments[j]
+
+ if (!segment) continue
+
+ while (currentLine < segment.line) {
+ segmentSep = ''
+ lastColumn = 0
+ mapping += ';'
+ currentLine++
+ }
+
+ mapping += segmentSep
+ mapping += toVlq(segment.column - lastColumn)
+ mapping += toVlq(0)
+ mapping += toVlq(segment.sourceLine - lastSourceLine)
+ mapping += toVlq(segment.sourceColumn - lastSourceColumn)
+
+ lastColumn = segment.column
+ lastSourceLine = segment.sourceLine
+ lastSourceColumn = segment.sourceColumn
+
+ segmentSep = ','
+ }
+ }
+
+ sm = {
+ version : 3,
+ file : '',
+ sourceRoot : '',
+ sources : [''],
+ sourcesContent: [null],
+ names : [],
+ mappings : mapping
+ }
+
+ if (cfg.file) {
+ sm.file = cfg.file
+ }
+
+ if (cfg.sourceRoot) {
+ sm.sourceRoot = cfg.sourceRoot
+ }
+
+ if (cfg.source) {
+ sm.sources = [cfg.source]
+ }
+
+ if (cfg.sourceContent) {
+ sm.sourcesContent = [cfg.sourceContent]
+ }
+
+ return JSON.stringify(sm)
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/behavior.js b/test/behavior.js
index 5ca37f3..f8d7eca 100644
--- a/test/behavior.js
+++ b/test/behavior.js
@@ -130,13 +130,13 @@ describe('Identifiers', function() {
expect(vars.val).not.toBe(R.T)
})
- it('are not identified as Ramda function if is a property', function() {
+ it('are not identified as Ramda function if it is a property', function() {
var vars = {}
run('(def obj {:T 0}) (alter vars.val obj.T)', vars)
expect(vars.val).not.toBe(R.T)
})
- it('are not identified as Ramda function if is previously defined', function() {
+ it('are not identified as Ramda function if it is previously defined', function() {
var vars = {}
run('(def T F) (alter vars.val (T))', vars)
expect(vars.val).toBe(false)
diff --git a/test/semantic.js b/test/semantic.js
index 69c8008..a3fbfb3 100644
--- a/test/semantic.js
+++ b/test/semantic.js
@@ -1,8 +1,12 @@
describe('Semantic analizer', function() {
+ var getCtx = function(src) {
+ return ram.compile(src, {printErrors: false}).ctx
+ }
+
it('adds error if other that S-Expression or identifier are used as operations', function() {
- var ctx = ram.compile('([]) ({}) (\'\') (nil) (0) (/x/)', null, true)
+ var ctx = getCtx('([]) ({}) (\'\') (nil) (0) (/x/)')
expect(contains(ctx.errors[0], T.ARRAY)).toBe(true)
expect(contains(ctx.errors[1], T.OBJECT)).toBe(true)
expect(contains(ctx.errors[2], T.STRING)).toBe(true)
@@ -14,103 +18,103 @@ describe('Semantic analizer', function() {
describe('`def`', function() {
it('can be used only in module scope', function() {
- var ctx = ram.compile('((def x))', null, true)
+ var ctx = getCtx('((def x))')
expect(contains(ctx.errors[0], 'def')).toBe(true)
})
it('must have arguments', function() {
- var ctx = ram.compile('(def)', null, true)
+ var ctx = getCtx('(def)')
expect(contains(ctx.errors[0], 'def')).toBe(true)
})
it('must have no more than two arguments', function() {
- var ctx = ram.compile('(def x y z)', null, true)
+ var ctx = getCtx('(def x y z)')
expect(contains(ctx.errors[0], 'def')).toBe(true)
})
it('should add error when define a var more than once', function() {
- var ctx = ram.compile('(def x) (def x)', null, true)
+ var ctx = getCtx('(def x) (def x)')
expect(contains(ctx.errors[0], 'x')).toBe(true)
})
it('should add error when define a qualified identifier', function() {
- var ctx = ram.compile('(def x.y)', null, true)
+ var ctx = getCtx('(def x.y)')
expect(contains(ctx.errors[0], 'x.y')).toBe(true)
})
})
describe('`import`', function() {
it('can be used only in module scope', function() {
- var ctx = ram.compile('((import x y))', null, true)
+ var ctx = getCtx('((import x y))')
expect(contains(ctx.errors[0], 'import')).toBe(true)
})
it('must have arguments', function() {
- var ctx = ram.compile('(import)', null, true)
+ var ctx = getCtx('(import)')
expect(contains(ctx.errors[0], 'import')).toBe(true)
})
it('must have exactly two arguments', function() {
- var ctx = ram.compile('(import x y z)', null, true)
+ var ctx = getCtx('(import x y z)')
expect(contains(ctx.errors[0], 'import')).toBe(true)
})
})
describe('`new`', function() {
it('can be used only in module scope', function() {
- var ctx = ram.compile('(new)', null, true)
+ var ctx = getCtx('(new)')
expect(contains(ctx.errors[0], 'new')).toBe(true)
})
})
describe('`fn`', function() {
it('must have a list as the first argument', function() {
- var ctx = ram.compile('(fn x)', null, true)
+ var ctx = getCtx('(fn x)')
expect(contains(ctx.errors[0], 'fn')).toBe(true)
})
it('must have a list as the first argument', function() {
- var ctx = ram.compile('(fn [3])', null, true)
+ var ctx = getCtx('(fn [3])')
expect(contains(ctx.errors[0], T.NUMBER)).toBe(true)
})
})
describe('`alter`', function() {
it('must have arguments', function() {
- var ctx = ram.compile('(alter)', null, true)
+ var ctx = getCtx('(alter)')
expect(contains(ctx.errors[0], 'alter')).toBe(true)
})
it('must have no more than two arguments', function() {
- var ctx = ram.compile('(alter x y z)', null, true)
+ var ctx = getCtx('(alter x y z)')
expect(contains(ctx.errors[0], 'alter')).toBe(true)
})
})
describe('`R` identifier', function() {
it('adds error if `R` is used as operator', function() {
- var ctx = ram.compile('(R)', null, true)
+ var ctx = getCtx('(R)')
expect(contains(ctx.errors[0], 'R')).toBe(true)
})
it('adds error if `R` is used in S-Expression', function() {
- var ctx = ram.compile('(x R)', null, true)
+ var ctx = getCtx('(x R)')
expect(contains(ctx.errors[0], 'R')).toBe(true)
})
it('adds error if `R` is used in Array', function() {
- var ctx = ram.compile('(x [R])', null, true)
+ var ctx = getCtx('(x [R])')
expect(contains(ctx.errors[0], 'R')).toBe(true)
})
it('adds error if `R` is used in Object', function() {
- var ctx = ram.compile('(x {:x R})', null, true)
+ var ctx = getCtx('(x {:x R})')
expect(contains(ctx.errors[0], 'R')).toBe(true)
})
})
it('adds error if ES keywords are used as identifier', function() {
- var ctx = ram.compile('(if let for const)', null, true)
+ var ctx = getCtx('(if let for const)')
expect(contains(ctx.errors[0], 'if')).toBe(true)
expect(contains(ctx.errors[1], 'let')).toBe(true)
expect(contains(ctx.errors[2], 'for')).toBe(true)
@@ -118,7 +122,7 @@ describe('Semantic analizer', function() {
})
it('adds error a builtin function is used as data in S-Expression', function() {
- var ctx = ram.compile('(x fn alter new)', null, true)
+ var ctx = getCtx('(x fn alter new)')
expect(contains(ctx.errors[0], 'fn')).toBe(true)
expect(contains(ctx.errors[1], 'alter')).toBe(true)
expect(contains(ctx.errors[2], 'new')).toBe(true)