walt-compiler
Advanced tools
Comparing version 0.3.1 to 0.3.2
{ | ||
"name": "walt-compiler", | ||
"version": "0.3.1", | ||
"version": "0.3.2", | ||
"description": "Alternative syntax for WebAssembly text format", | ||
@@ -5,0 +5,0 @@ "main": "dist/walt.js", |
@@ -24,3 +24,3 @@ import test from "ava"; | ||
// This just needs to compile | ||
export function testUninitializedLocals() { const x: i32; } | ||
export function testUninitializedLocals() { let x: i32; } | ||
// This also tests built-in words in function names ("void") | ||
@@ -67,7 +67,14 @@ export function testVoidIsOptional() {} | ||
const source = ` | ||
const table: Table = { element: anyfunc, initial: 2 }; | ||
type lambda Lambda = (i32, i32) => i32; | ||
type lambda SimpleLambda = () => i32; | ||
const table: Table = { element: anyfunc, initial: 5 }; | ||
type Func = (i32, i32) => i32; | ||
type Simple = () => i32; | ||
type Void = () => void; | ||
type ArgsOnly = (i32, i32) => void; | ||
function getSimpleLambda(): SimpleLambda { | ||
type Closure = Lambda<Func>; | ||
type SimpleClosure = Lambda<Simple>; | ||
type VoidClosure = Lambda<Void>; | ||
type ArgsOnlyClosure = Lambda<ArgsOnly>; | ||
function getSimpleLambda(): SimpleClosure { | ||
let x: i32 = 0; | ||
@@ -80,3 +87,3 @@ return (): i32 => { | ||
function getLambda(): Lambda { | ||
function getLambda(): Closure { | ||
// close over two locals | ||
@@ -90,8 +97,23 @@ let x: i32 = 0; | ||
// Closures below are not useful, but necessary to cover all scenarios | ||
function getVoidLamba(): VoidClosure { | ||
let x: i32 = 0; | ||
return () => { | ||
x += 1; | ||
} | ||
} | ||
function getArgsOnlyLambda(): ArgsOnlyClosure { | ||
let x: i32 = 0; | ||
return (z: i32, y: i32) => { | ||
x+= z + y; | ||
} | ||
} | ||
export function test(): i32 { | ||
const closure: Lambda = getLambda(); | ||
const closure: Closure = getLambda(); | ||
// should be 5 | ||
const x: i32 = closure(2, 3); | ||
const closure2: SimpleLambda = getSimpleLambda(); | ||
const closure2: SimpleClosure = getSimpleLambda(); | ||
// should be 1 | ||
@@ -98,0 +120,0 @@ closure2(); |
import test from "ava"; | ||
import compile from ".."; | ||
import statement from "../parser/statement"; | ||
import parseFunction from "../parser/maybe-function-declaration"; | ||
import { TYPE_ARRAY, LOCAL_INDEX_MAP } from "../semantics/metadata"; | ||
import { mockContext } from "../utils/mocks"; | ||
@@ -13,18 +9,6 @@ const compileAndRun = (src, imports) => | ||
test("memory parser", t => { | ||
const ctx = mockContext(`function test(): i32 { | ||
let x: i32[] = 0; | ||
let y: i32 = 5; | ||
x[0] = 21; | ||
x[y] = 2; | ||
return x[0] * x[y]; | ||
}`); | ||
const node = parseFunction(ctx); | ||
t.snapshot(node); | ||
}); | ||
test("memory can be defined", t => | ||
compileAndRun( | ||
` | ||
const memory: Memory = { 'initial': 2 }; | ||
const memory: Memory = { 'initial': 2, max: 2 }; | ||
@@ -55,28 +39,1 @@ export function test(): i32 { | ||
}); | ||
test("memory max can be set", () => | ||
compileAndRun("const memory: Memory = { 'initial': 1, 'max': 2 };")); | ||
test("memory store on float arrays", t => { | ||
const ctx = mockContext("x[0] = 2.0;"); | ||
ctx.func = { | ||
params: [], | ||
meta: [ | ||
{ | ||
type: LOCAL_INDEX_MAP, | ||
payload: { | ||
x: { | ||
node: { | ||
value: "x", | ||
type: "i32", | ||
meta: [{ type: TYPE_ARRAY, payload: "f32" }], | ||
params: [], | ||
}, | ||
}, | ||
}, | ||
}, | ||
], | ||
}; | ||
const node = statement(ctx); | ||
t.snapshot(node); | ||
}); |
@@ -17,3 +17,3 @@ // @flow | ||
const ptr: i32 = heapPointer; | ||
heapPointer += 8; | ||
heapPointer += size; | ||
return ptr; | ||
@@ -20,0 +20,0 @@ } |
// @flow | ||
import { i32, i64, f32 } from "wasm-types"; | ||
import { i32, i64, f32, f64 } from "wasm-types"; | ||
import type { RawOpcodeType } from "../generator/flow/types"; | ||
@@ -70,3 +70,3 @@ | ||
opcode(f32, i32, ___, 4, 0x2a, "f32Load", "f32.load"); | ||
opcode(f32, i32, ___, 8, 0x2b, "f64Load", "f64.load"); | ||
opcode(f64, i32, ___, 8, 0x2b, "f64Load", "f64.load"); | ||
opcode(i32, i32, ___, 1, 0x2c, "i32Load8S", "i32.load8_s"); | ||
@@ -96,3 +96,3 @@ opcode(i32, i32, ___, 1, 0x2d, "i32Load8U", "i32.load8_u"); | ||
opcode(f32, ___, ___, 0, 0x43, "f32Const", "f32.const"); | ||
opcode(f32, ___, ___, 0, 0x44, "f64Const", "f64.const"); | ||
opcode(f64, ___, ___, 0, 0x44, "f64Const", "f64.const"); | ||
opcode(i32, i32, ___, 0, 0x45, "i32Eqz", "i32.eqz"); | ||
@@ -189,9 +189,9 @@ opcode(i32, i32, i32, 0, 0x46, "i32Eq", "i32.eq"); | ||
opcode(f32, f32, f32, 0, 0x9f, "f32Sqrt", "f64.sqrt"); | ||
opcode(f32, f32, f32, 0, 0xa0, "f64Add", "f64.add"); | ||
opcode(f32, f32, f32, 0, 0xa1, "f64Sub", "f64.sub"); | ||
opcode(f32, f32, f32, 0, 0xa2, "f64Mul", "f64.mul"); | ||
opcode(f32, f32, f32, 0, 0xa3, "f64Div", "f64.div"); | ||
opcode(f32, f32, f32, 0, 0xa4, "f64Min", "f64.min"); | ||
opcode(f32, f32, f32, 0, 0xa5, "f64Max", "f64.max"); | ||
opcode(f32, f32, f32, 0, 0xa6, "f64Copysign", "f64.copysign"); | ||
opcode(f64, f64, f64, 0, 0xa0, "f64Add", "f64.add"); | ||
opcode(f64, f64, f64, 0, 0xa1, "f64Sub", "f64.sub"); | ||
opcode(f64, f64, f64, 0, 0xa2, "f64Mul", "f64.mul"); | ||
opcode(f64, f64, f64, 0, 0xa3, "f64Div", "f64.div"); | ||
opcode(f64, f64, f64, 0, 0xa4, "f64Min", "f64.min"); | ||
opcode(f64, f64, f64, 0, 0xa5, "f64Max", "f64.max"); | ||
opcode(f64, f64, f64, 0, 0xa6, "f64Copysign", "f64.copysign"); | ||
opcode(i32, i64, ___, 0, 0xa7, "i32Wrapi64", "i32.wrap/i64"); | ||
@@ -198,0 +198,0 @@ opcode(i32, f32, ___, 0, 0xa8, "i32TruncSf32", "i32.trunc_s/f32"); |
@@ -5,3 +5,3 @@ // @flow | ||
import generateExpression from "./expression"; | ||
import { generateValueType, isBuiltinType } from "./utils"; | ||
import { isBuiltinType } from "./utils"; | ||
import opcode from "../emitter/opcode"; | ||
@@ -17,6 +17,2 @@ import { get, LOCAL_INDEX } from "../semantics/metadata"; | ||
if (parent && Array.isArray(parent.locals)) { | ||
parent.locals.push(generateValueType(node)); | ||
} | ||
if (initNode) { | ||
@@ -23,0 +19,0 @@ const metaIndex = get(LOCAL_INDEX, node); |
@@ -15,4 +15,4 @@ // @flow | ||
import generateType from "./type"; | ||
import { generateValueType } from "./utils"; | ||
import { generateImplicitFunctionType } from "./type"; | ||
import { | ||
@@ -22,2 +22,3 @@ get, | ||
FUNCTION_INDEX, | ||
FUNCTION_METADATA, | ||
typeIndex as setMetaTypeIndex, | ||
@@ -38,7 +39,19 @@ } from "../semantics/metadata"; | ||
const metadata = get(FUNCTION_METADATA, func); | ||
invariant(body, "Cannot generate code for function without body"); | ||
invariant(metadata, "Cannot generate code for function without metadata"); | ||
const { locals, argumentsCount } = metadata.payload; | ||
const block = { | ||
code: [], | ||
locals: [], | ||
// On this Episode of ECMAScript Spec: Object own keys traversal! | ||
// Sometimes it pays to know the spec. Keys are traversed in the order | ||
// they are added to the object. This includes Object.keys. Because the AST is traversed | ||
// depth-first we can guarantee that arguments will also be added first | ||
// to the locals object. We can depend on the spec providing the keys, | ||
// such that we can slice away the number of arguments and get DECLARED locals _only_. | ||
locals: Object.keys(locals) | ||
.slice(argumentsCount) | ||
.map(key => generateValueType(locals[key])), | ||
debug: `Function ${func.value}`, | ||
@@ -45,0 +58,0 @@ }; |
// @flow | ||
import Syntax from "../Syntax"; | ||
import { generateValueType } from "./utils"; | ||
@@ -9,16 +8,12 @@ import { I32, I64, F32, F64 } from "../emitter/value_type"; | ||
const _global = generateValueType(node); | ||
if (node.params.length > 0) { | ||
const { Type, value } = node.params[0]; | ||
if (Type === Syntax.Constant) { | ||
switch (_global.type) { | ||
case F32: | ||
case F64: | ||
_global.init = parseFloat(value); | ||
break; | ||
case I32: | ||
case I64: | ||
default: | ||
_global.init = parseInt(value); | ||
} | ||
} | ||
const { value } = node.params[0]; | ||
switch (_global.type) { | ||
case F32: | ||
case F64: | ||
_global.init = parseFloat(value); | ||
break; | ||
case I32: | ||
case I64: | ||
default: | ||
_global.init = parseInt(value); | ||
} | ||
@@ -25,0 +20,0 @@ |
@@ -24,2 +24,3 @@ // @flow | ||
const semanticAST = semantics(ast); | ||
// console.log(printNode(semanticAST)); | ||
validate( | ||
@@ -26,0 +27,0 @@ semanticAST, |
@@ -11,3 +11,4 @@ # Snapshot report for `src/parser/__tests__/context-spec.js` | ||
`SyntaxError: let x: i32 = someUnknownToken;␊ | ||
`SyntaxError: ␊ | ||
let x: i32 = someUnknownToken;␊ | ||
^^^^^^^^^^^^^^^^^ unknown token␊ | ||
@@ -21,3 +22,4 @@ Test Error␊ | ||
`SyntaxError: let x: i32 = someUnknownToken;␊ | ||
`SyntaxError: ␊ | ||
let x: i32 = someUnknownToken;␊ | ||
^^^^^^^^^^^^^^^^^ Unexpected token Identifier␊ | ||
@@ -31,5 +33,6 @@ Expected: "someUnknownToken"␊ | ||
`SyntaxError: let x: i32 = someUnknownToken;␊ | ||
`SyntaxError: ␊ | ||
let x: i32 = someUnknownToken;␊ | ||
^^^^^^^^^^^^^^^^^ someUnknownToken␊ | ||
Unknown token␊ | ||
at unknown (unknown:1:13)` |
@@ -101,3 +101,3 @@ # Snapshot report for `src/parser/__tests__/sizeof-spec.js` | ||
0000005f: a ; Code section␊ | ||
00000060: 29 ; size␊ | ||
00000060: 3c ; size␊ | ||
00000061: 4 ; function count␊ | ||
@@ -117,3 +117,3 @@ 00000062: c ; Function _32BitSizes␊ | ||
0000006e: b ; end␊ | ||
0000006f: c ; Function _64BitSizes␊ | ||
0000006f: 1b ; Function _64BitSizes␊ | ||
00000070: 2 ; locals count␊ | ||
@@ -124,23 +124,35 @@ 00000071: 1 ; number of locals of following type␊ | ||
00000074: 7c ; f64␊ | ||
00000075: 41 ; i32.const ␊ | ||
00000076: 8 ; i32.literal␊ | ||
00000077: 41 ; i32.const ␊ | ||
00000078: 8 ; i32.literal␊ | ||
00000079: 6a ; i32.add ␊ | ||
0000007a: f ; return ␊ | ||
0000007b: b ; end␊ | ||
0000007c: 7 ; Function userDefinedObject␊ | ||
0000007d: 1 ; locals count␊ | ||
0000007e: 1 ; number of locals of following type␊ | ||
0000007f: 7f ; i32␊ | ||
00000080: 41 ; i32.const ␊ | ||
00000081: 10 ; i32.literal␊ | ||
00000082: f ; return ␊ | ||
00000083: b ; end␊ | ||
00000084: 5 ; Function userDefinedTypeName␊ | ||
00000085: 0 ; locals count␊ | ||
00000075: 42 ; i64.const ␊ | ||
00000076: 0 ; i64.literal␊ | ||
00000077: 21 ; set_local x<i64>␊ | ||
00000078: 0 ; i32.literal␊ | ||
00000079: 44 ; f64.const ␊ | ||
0000007a: 0 ; f64.literal␊ | ||
00000082: 21 ; set_local y<f64>␊ | ||
00000083: 1 ; i32.literal␊ | ||
00000084: 41 ; i32.const ␊ | ||
00000085: 8 ; i32.literal␊ | ||
00000086: 41 ; i32.const ␊ | ||
00000087: 10 ; i32.literal␊ | ||
00000088: f ; return ␊ | ||
00000089: b ; end␊ | ||
00000087: 8 ; i32.literal␊ | ||
00000088: 6a ; i32.add ␊ | ||
00000089: f ; return ␊ | ||
0000008a: b ; end␊ | ||
0000008b: b ; Function userDefinedObject␊ | ||
0000008c: 1 ; locals count␊ | ||
0000008d: 1 ; number of locals of following type␊ | ||
0000008e: 7f ; i32␊ | ||
0000008f: 41 ; i32.const ␊ | ||
00000090: 0 ; i32.literal␊ | ||
00000091: 21 ; set_local x<Type>␊ | ||
00000092: 0 ; i32.literal␊ | ||
00000093: 41 ; i32.const ␊ | ||
00000094: 10 ; i32.literal␊ | ||
00000095: f ; return ␊ | ||
00000096: b ; end␊ | ||
00000097: 5 ; Function userDefinedTypeName␊ | ||
00000098: 0 ; locals count␊ | ||
00000099: 41 ; i32.const ␊ | ||
0000009a: 10 ; i32.literal␊ | ||
0000009b: f ; return ␊ | ||
0000009c: b ; end␊ | ||
============ fin =============` |
@@ -12,3 +12,4 @@ # Snapshot report for `src/parser/__tests__/type-spec.js` | ||
SyntaxError { | ||
message: `type Type = i32 => void;␊ | ||
message: `␊ | ||
type Type = i32 => void;␊ | ||
^^^^^^^^^^^^^^^ A function type must be of form (<type>, ...) <type>␊ | ||
@@ -15,0 +16,0 @@ Invalid type syntax␊ |
@@ -6,4 +6,4 @@ import test from "ava"; | ||
const walt = `export function _32BitSizes(): i32 { | ||
const x: i32; | ||
const y: f32; | ||
let x: i32; | ||
let y: f32; | ||
return sizeof(x) + sizeof(y); | ||
@@ -13,4 +13,4 @@ } | ||
export function _64BitSizes(): i32 { | ||
const x: i64; | ||
const y: f64; | ||
const x: i64 = 0; | ||
const y: f64 = 0; | ||
return sizeof(x) + sizeof(y); | ||
@@ -20,3 +20,3 @@ } | ||
export function userDefinedObject(): i32 { | ||
const x: Type; | ||
const x: Type = 0; | ||
return sizeof(x); | ||
@@ -23,0 +23,0 @@ } |
@@ -40,3 +40,3 @@ // @flow | ||
let params = []; | ||
let baseParams = []; | ||
const lambda = { | ||
@@ -50,8 +50,9 @@ ...op, | ||
}; | ||
const [lhs, rhs] = args.params; | ||
// The reason why this is so tricky to parse is because there are too many | ||
// optional parts of a coluse definition, like arguments and return type | ||
if (args.Type === Syntax.Pair) { | ||
const [lhs, rhs] = args.params; | ||
if (lhs != null && rhs != null) { | ||
params = | ||
baseParams = | ||
lhs.Type === Syntax.Pair | ||
@@ -63,43 +64,24 @@ ? [makeArgs(lhs), makeResult(rhs)] | ||
]; | ||
return { | ||
...lambda, | ||
params: [ | ||
{ | ||
...lambda, | ||
Type: Syntax.FunctionDeclaration, | ||
params: [...params, block], | ||
}, | ||
], | ||
}; | ||
} else { | ||
baseParams = [makeArgs(null), makeResult(lhs)]; | ||
} | ||
return { | ||
...lambda, | ||
params: [ | ||
{ | ||
...lambda, | ||
Type: Syntax.FunctionDeclaration, | ||
params: [makeArgs(null), makeResult(lhs), block], | ||
}, | ||
], | ||
}; | ||
} else if (args.Type === Syntax.Sequence) { | ||
return { | ||
...lambda, | ||
params: [ | ||
{ | ||
...lambda, | ||
Type: Syntax.FunctionDeclaration, | ||
params: [ | ||
makeArgs(args), | ||
makeResult(result.Type === Syntax.Type ? result : null), | ||
block, | ||
], | ||
}, | ||
], | ||
}; | ||
baseParams = [ | ||
makeArgs(args), | ||
makeResult(result.Type === Syntax.Type ? result : null), | ||
]; | ||
} else { | ||
baseParams = [makeArgs(null), makeResult(null)]; | ||
} | ||
return lambda; | ||
return { | ||
...lambda, | ||
params: [ | ||
{ | ||
...lambda, | ||
Type: Syntax.FunctionDeclaration, | ||
params: [...baseParams, block], | ||
}, | ||
], | ||
}; | ||
} |
@@ -35,6 +35,2 @@ // @flow | ||
if (node.const && !node.init) { | ||
throw ctx.syntaxError("Constant value must be initialized"); | ||
} | ||
return ctx.endNode({ ...node, params, type }, Type); | ||
@@ -41,0 +37,0 @@ }; |
@@ -6,3 +6,2 @@ // @flow | ||
import generateError from "../utils/generate-error"; | ||
import { closureType } from "../semantics/metadata"; | ||
import type { NodeType } from "../flow/types"; | ||
@@ -14,6 +13,2 @@ | ||
const meta = []; | ||
const isClosure = ctx.eat(["lambda"]); | ||
if (isClosure) { | ||
meta.push(closureType(true)); | ||
} | ||
@@ -23,2 +18,20 @@ const value = ctx.expect(null, Syntax.Identifier).value; | ||
const maybeGeneric = ctx.token.value; | ||
// Generic Type | ||
if (ctx.eat(null, Syntax.Identifier)) { | ||
ctx.expect(["<"]); | ||
const idNode = ctx.makeNode( | ||
{ ...ctx.token, type: null }, | ||
Syntax.Identifier | ||
); | ||
ctx.expect(null, Syntax.Identifier); | ||
ctx.expect([">"]); | ||
const genericTypeNode = ctx.endNode( | ||
{ ...node, value, params: [{ ...idNode, value: maybeGeneric }, idNode] }, | ||
Syntax.GenericType | ||
); | ||
return genericTypeNode; | ||
} | ||
// Regular function type definition | ||
@@ -43,9 +56,2 @@ if (ctx.eat(["("])) { | ||
if (isClosure) { | ||
args.params = [ | ||
{ ...args, params: [], type: "i32", value: "i32", Type: Syntax.Type }, | ||
...args.params, | ||
]; | ||
} | ||
ctx.expect([")"]); | ||
@@ -52,0 +58,0 @@ ctx.expect(["=>"]); |
@@ -16,3 +16,2 @@ /** | ||
import Syntax from "../Syntax"; | ||
import walkNode from "../utils/walk-node"; | ||
import mapNode from "../utils/map-node"; | ||
@@ -24,2 +23,3 @@ import { mapImport } from "./map-import"; | ||
import mapStructNode from "./map-struct"; | ||
import { mapGeneric } from "./map-generic"; | ||
import hasNode from "../utils/has-node"; | ||
@@ -42,6 +42,8 @@ import { astMeta } from "./metadata"; | ||
// Types have to be pre-parsed before the rest of the program | ||
walkNode({ | ||
[Syntax.Typedef]: node => { | ||
const astWithTypes = mapNode({ | ||
[Syntax.Typedef]: (node, _) => { | ||
types[node.value] = node; | ||
return node; | ||
}, | ||
[Syntax.GenericType]: mapGeneric({ types }), | ||
})(ast); | ||
@@ -65,3 +67,3 @@ | ||
}), | ||
})(ast); | ||
})(astWithTypes); | ||
@@ -68,0 +70,0 @@ return { |
@@ -31,2 +31,3 @@ // @flow | ||
CLOSURE_TYPE, | ||
FUNCTION_METADATA, | ||
} from "../metadata"; | ||
@@ -42,2 +43,4 @@ import type { NodeType } from "../../flow/types"; | ||
const locals = {}; | ||
// Count the number of arguments to help with generating bytecode | ||
let argumentsCount = 0; | ||
const closures = { | ||
@@ -57,2 +60,3 @@ // Capture all enclosed variables if any | ||
[Syntax.Pair]: pairNode => { | ||
argumentsCount += 1; | ||
const [identifierNode, typeNode] = pairNode.params; | ||
@@ -98,3 +102,15 @@ const withTypeApplied = { | ||
})(), | ||
meta: [...node.meta, setMetaFunctionIndex(Object.keys(functions).length)], | ||
meta: [ | ||
...node.meta, | ||
setMetaFunctionIndex(Object.keys(functions).length), | ||
{ | ||
type: FUNCTION_METADATA, | ||
payload: { | ||
locals, | ||
get argumentsCount() { | ||
return argumentsCount; | ||
}, | ||
}, | ||
}, | ||
], | ||
// If we are generating closures for this function, then we need to inject a | ||
@@ -101,0 +117,0 @@ // declaration for the environment local. This local cannot be referenced or |
// @flow | ||
import Syntax from "../../Syntax"; | ||
import { typeCast } from "../metadata"; | ||
import generateErrorString from "../../utils/generate-error"; | ||
import type { NodeType } from "../../flow/types"; | ||
@@ -34,31 +33,5 @@ | ||
if (type == null) { | ||
const [start, end] = expression.range; | ||
throw new SyntaxError( | ||
generateErrorString( | ||
"Cannot generate expression, missing type information", | ||
"Missing type information", | ||
{ start, end }, | ||
"", | ||
"" | ||
) | ||
); | ||
} | ||
// iterate again, this time, patching any mis-typed nodes | ||
const params = expression.params.map(paramNode => { | ||
if (paramNode.type == null) { | ||
const [start, end] = paramNode.range; | ||
throw new SyntaxError( | ||
generateErrorString( | ||
"Could not infer a type in binary expression", | ||
`${paramNode.value} has no defined type`, | ||
{ start, end }, | ||
"", | ||
"" | ||
) | ||
); | ||
} | ||
if (paramNode.type !== type && type != null) { | ||
if (paramNode.type != null && paramNode.type !== type && type != null) { | ||
// last check is for flow | ||
@@ -65,0 +38,0 @@ return { |
@@ -26,2 +26,3 @@ // @flow | ||
export const AST_METADATA = "@@global/ast"; | ||
export const FUNCTION_METADATA = "@@function/meta"; | ||
export const ALIAS = "alias"; | ||
@@ -28,0 +29,0 @@ |
@@ -22,2 +22,3 @@ // @flow | ||
export const Type = "Type"; | ||
export const GenericType = "GenericType"; | ||
export const UserType = "UserType"; | ||
@@ -126,2 +127,3 @@ export const FunctionType = "FunctionType"; | ||
Type, | ||
GenericType, | ||
UserType, | ||
@@ -128,0 +130,0 @@ FunctionType, |
@@ -468,23 +468,2 @@ # Snapshot report for `src/tokenizer/__tests__/tokenizer-spec.js` | ||
## parsers strings within strings | ||
> Snapshot 1 | ||
[ | ||
{ | ||
end: { | ||
col: 37, | ||
line: 1, | ||
sourceLine: '"here is a string with a \'substring\'"', | ||
}, | ||
start: { | ||
col: 0, | ||
line: 1, | ||
sourceLine: '"here is a string with a \'substring\'"', | ||
}, | ||
type: 'StringLiteral', | ||
value: '"here is a string with a \'substring\'"', | ||
}, | ||
] | ||
## parses a stream into tokens | ||
@@ -650,1 +629,57 @@ | ||
] | ||
## parses strings with escaped string | ||
> Snapshot 1 | ||
[ | ||
{ | ||
end: { | ||
col: 28, | ||
line: 1, | ||
sourceLine: '"string start \\" string end" \'start \\\' end \'', | ||
}, | ||
start: { | ||
col: 0, | ||
line: 1, | ||
sourceLine: '"string start \\" string end" \'start \\\' end \'', | ||
}, | ||
type: 'StringLiteral', | ||
value: '"string start \\" string end"', | ||
}, | ||
{ | ||
end: { | ||
col: 44, | ||
line: 1, | ||
sourceLine: '"string start \\" string end" \'start \\\' end \'', | ||
}, | ||
start: { | ||
col: 29, | ||
line: 1, | ||
sourceLine: '"string start \\" string end" \'start \\\' end \'', | ||
}, | ||
type: 'StringLiteral', | ||
value: '\'start \\\' end \'', | ||
}, | ||
] | ||
## parses strings within strings | ||
> Snapshot 1 | ||
[ | ||
{ | ||
end: { | ||
col: 37, | ||
line: 1, | ||
sourceLine: '"here is a string with a \'substring\'"', | ||
}, | ||
start: { | ||
col: 0, | ||
line: 1, | ||
sourceLine: '"here is a string with a \'substring\'"', | ||
}, | ||
type: 'StringLiteral', | ||
value: '"here is a string with a \'substring\'"', | ||
}, | ||
] |
@@ -103,3 +103,3 @@ import Tokenizer from ".."; | ||
test("parsers strings within strings", t => { | ||
test("parses strings within strings", t => { | ||
const stream = new Stream("\"here is a string with a 'substring'\""); | ||
@@ -110,2 +110,9 @@ const tokenizer = new Tokenizer(stream); | ||
test("parses strings with escaped string", t => { | ||
// eslint-disable-next-line | ||
const stream = new Stream(`"string start \\" string end" 'start \\' end '`); | ||
const tokenizer = new Tokenizer(stream); | ||
t.snapshot(tokenizer.parse()); | ||
}); | ||
test("parses identifiers with numbers", t => { | ||
@@ -112,0 +119,0 @@ const stream = new Stream("test1foo42bar "); |
@@ -9,3 +9,3 @@ // @flow | ||
const endsInSingleQuote = char => { | ||
if (char === "\\") { | ||
if (/\\/.test(char)) { | ||
return quoteOK(endsInSingleQuote); | ||
@@ -21,3 +21,3 @@ } | ||
const endsInDoubleQuote = char => { | ||
if (char === "\\") { | ||
if (/\\/.test(char)) { | ||
return quoteOK(endsInDoubleQuote); | ||
@@ -24,0 +24,0 @@ } |
@@ -24,2 +24,3 @@ // @flow | ||
return ( | ||
"\n" + | ||
Line + | ||
@@ -26,0 +27,0 @@ "\n" + |
@@ -22,2 +22,3 @@ # Snapshot report for `src/validation/__tests__/validation-spec.js` | ||
␊ | ||
␊ | ||
x = 1;␊ | ||
@@ -28,2 +29,3 @@ ^^^ const is a convenience type and cannot be reassigned, use let instead. NOTE: All locals in WebAssembly are mutable.␊ | ||
␊ | ||
␊ | ||
y += 1;␊ | ||
@@ -36,2 +38,23 @@ ^^^^ const is a convenience type and cannot be reassigned, use let instead. NOTE: All locals in WebAssembly are mutable.␊ | ||
## constants must be initialized | ||
> Snapshot 1 | ||
Error { | ||
message: `Cannot generate WebAssembly for spec.walt. 2 problems.␊ | ||
␊ | ||
␊ | ||
const g: i32 = 2 + 2;␊ | ||
^^^^^^^^^^^^^^^^^^^^^ WebAssembly does not allow for non number literal constant initializers.␊ | ||
Global Constants must be initialized with a Number literal.␊ | ||
at global (spec.walt:2:4)␊ | ||
␊ | ||
␊ | ||
const x: i32;␊ | ||
^^^^^^^^^^^^^ Local Constants must be initialized with an expression.␊ | ||
Constant declaration without an initializer.␊ | ||
at global (spec.walt:4:6)␊ | ||
`, | ||
} | ||
## functions must be defined | ||
@@ -44,2 +67,3 @@ | ||
␊ | ||
␊ | ||
ptr();␊ | ||
@@ -50,2 +74,3 @@ ^^^^^ ptr has type Type which is not defined. Inidrect calls must have pre-defined types.␊ | ||
␊ | ||
␊ | ||
return notDefined();␊ | ||
@@ -56,2 +81,3 @@ ^^^^^^^^^^^^ notDefined is not defined.␊ | ||
␊ | ||
␊ | ||
return notDefined();␊ | ||
@@ -71,2 +97,3 @@ ^^^^^^^^^^^^^^^^^^^ Functions in WebAssembly must have a consistent return value. Expected i32 received null␊ | ||
␊ | ||
␊ | ||
return;␊ | ||
@@ -77,2 +104,3 @@ ^^^^^^^ Functions in WebAssembly must have a consistent return value. Expected i32 received null␊ | ||
␊ | ||
␊ | ||
return x;␊ | ||
@@ -90,4 +118,5 @@ ^^^^^^^^^ Functions in WebAssembly must have a consistent return value. Expected i32 received i64␊ | ||
Error { | ||
message: `Cannot generate WebAssembly for spec.walt. 1 problems.␊ | ||
message: `Cannot generate WebAssembly for spec.walt. 2 problems.␊ | ||
␊ | ||
␊ | ||
export const x: i32;␊ | ||
@@ -97,2 +126,8 @@ ^^^^^^^^^^^^^ ␊ | ||
at global (spec.walt:1:7)␊ | ||
␊ | ||
␊ | ||
export const x: i32;␊ | ||
^^^^^^^^^^^^^ Global constants must be initialized with a Number literal.␊ | ||
Constant declaration without an initializer.␊ | ||
at global (spec.walt:1:7)␊ | ||
`, | ||
@@ -108,2 +143,3 @@ } | ||
␊ | ||
␊ | ||
expost const x: i32;␊ | ||
@@ -123,2 +159,3 @@ ^^ ␊ | ||
␊ | ||
␊ | ||
obj = { y: 5 };␊ | ||
@@ -129,2 +166,3 @@ ^^^^^ Undefined key y for type T␊ | ||
␊ | ||
␊ | ||
obj.y = 5;␊ | ||
@@ -144,2 +182,3 @@ ^^^^ Undefined key y for type T␊ | ||
␊ | ||
␊ | ||
import { foo: Type } from 'env';␊ | ||
@@ -159,2 +198,3 @@ ^^^^^^ Invalid Import. Type type does not exist␊ | ||
␊ | ||
␊ | ||
y = 3 + 3;␊ | ||
@@ -174,2 +214,3 @@ ^^^ Statments cannot be used in assignment expressions. Did you miss a semicolon?␊ | ||
␊ | ||
␊ | ||
const x: i32 = 0␊ | ||
@@ -176,0 +217,0 @@ x = 2;␊ |
@@ -112,1 +112,13 @@ import test from "ava"; | ||
}); | ||
test("constants must be initialized", t => { | ||
const error = t.throws(() => | ||
parseAndValidate(` | ||
const g: i32 = 2 + 2; | ||
function test() { | ||
const x: i32; | ||
} | ||
`) | ||
); | ||
t.snapshot(error); | ||
}); |
@@ -8,3 +8,2 @@ // @flow | ||
import { get, GLOBAL_INDEX, TYPE_CONST, ALIAS } from "../semantics/metadata"; | ||
import type { NodeType } from "../flow/types"; | ||
@@ -82,3 +81,31 @@ | ||
[Syntax.ImmutableDeclaration]: (_, __) => {}, | ||
[Syntax.Declaration]: (_, __) => {}, | ||
[Syntax.Declaration]: (decl, _validator) => { | ||
const [initializer] = decl.params; | ||
if (get(TYPE_CONST, decl) != null) { | ||
const [start, end] = decl.range; | ||
if (initializer != null && initializer.Type !== Syntax.Constant) { | ||
problems.push( | ||
error( | ||
"Global Constants must be initialized with a Number literal.", | ||
"WebAssembly does not allow for non number literal constant initializers.", | ||
{ start, end }, | ||
filename, | ||
GLOBAL_LABEL | ||
) | ||
); | ||
} | ||
if (initializer == null) { | ||
problems.push( | ||
error( | ||
"Constant declaration without an initializer.", | ||
"Global constants must be initialized with a Number literal.", | ||
{ start, end }, | ||
filename, | ||
GLOBAL_LABEL | ||
) | ||
); | ||
} | ||
} | ||
}, | ||
[Syntax.FunctionDeclaration]: (func, __) => { | ||
@@ -104,2 +131,17 @@ const functionName = `${func.value}()`; | ||
} | ||
if (get(TYPE_CONST, node) != null) { | ||
const [start, end] = node.range; | ||
if (initializer == null) { | ||
problems.push( | ||
error( | ||
"Constant declaration without an initializer.", | ||
"Local Constants must be initialized with an expression.", | ||
{ start, end }, | ||
filename, | ||
GLOBAL_LABEL | ||
) | ||
); | ||
} | ||
} | ||
}, | ||
@@ -106,0 +148,0 @@ [Syntax.Assignment]: node => { |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
11574741
278
12374