Skip to content

Commit

Permalink
support fixed length types and signed int
Browse files Browse the repository at this point in the history
BREAKING CHANGE: mark uint with `uint` expression instead of former `uint2str`
  • Loading branch information
jfschwarz committed Feb 23, 2022
1 parent 676c882 commit 18b657d
Show file tree
Hide file tree
Showing 10 changed files with 229 additions and 131 deletions.
115 changes: 74 additions & 41 deletions src/compile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,29 @@ import { format } from "prettier"
import prettierPluginSolidity from "prettier-plugin-solidity"

type UnknownInput = { type: undefined }
type StringInput = { type: "string" }
type UintInput = { type: "uint" }
type StringInput = { type: "string"; length?: number }
type UintInput = { type: "uint"; length?: number }
type IntInput = { type: "int"; length?: number }
type BoolInput = { type: "bool" }

type ArrayInput = {
type: "array"
elementType:
| StringInput
| BoolInput
| UintInput
| StructInput
| ArrayInput
| UnknownInput
length?: number
elementType: InputType
}
type StructInput = {
type: "struct"
members: {
[field: string]:
| StringInput
| BoolInput
| UintInput
| ArrayInput
| StructInput
| UnknownInput
[field: string]: InputType
}
}

type InputType =
| UnknownInput
| StringInput
| BoolInput
| UintInput
| IntInput
| ArrayInput
| StructInput

Expand Down Expand Up @@ -120,7 +113,7 @@ ${options.contract ? "contract" : "library"} ${options.name || "Template"} {
${partialDefs}
${SOL_UINT2STR}
${SOL_INT_TO_STRING}
}
`
return format(solidityCode, {
Expand Down Expand Up @@ -206,10 +199,19 @@ ${options.contract ? "contract" : "library"} ${options.name || "Template"} {
}

const path = statement.path as AST.PathExpression
if (path.original === "uint2str") {
const intMatch = path.original.match(/^(uint|int)(\d*)$/)
const bytesMatch = path.original.match(/^bytes(\d*)$/)
if (intMatch) {
const type = intMatch[1] as "uint" | "int"
const length = parseInt(intMatch[2])
const fullPath = scope.resolve(statement.params[0] as AST.PathExpression)
narrowInput(scope.inputType, fullPath, type, length)
return [{ append: [`${type}ToString(${fullPath})`] }]
} else if (bytesMatch) {
const length = parseInt(bytesMatch[1])
const fullPath = scope.resolve(statement.params[0] as AST.PathExpression)
narrowInput(scope.inputType, fullPath, "uint")
return [{ append: [`uint2str(${fullPath})`] }]
narrowInput(scope.inputType, fullPath, "string", length)
return [{ append: [fullPath] }]
} else {
const fullPath = scope.resolve(path)
narrowInput(scope.inputType, fullPath, "string")
Expand Down Expand Up @@ -285,12 +287,19 @@ ${options.contract ? "contract" : "library"} ${options.name || "Template"} {
statement: AST.BlockStatement,
scope: Scope
): Output[] {
const path = statement.params[0]
const { params, program, hash } = statement
const path = params[0]
if (path.type !== "PathExpression") throw new Error("Unsupported")
const pathExpr = path as AST.PathExpression

const lengthHashValue = hash?.pairs?.find((p) => p.key === "length")?.value
const length =
lengthHashValue?.type === "NumberLiteral"
? (lengthHashValue as AST.NumberLiteral).value
: undefined

const iterateeResolvedPath = scope.resolve(pathExpr)
narrowInput(scope.inputType, iterateeResolvedPath, "array")
narrowInput(scope.inputType, iterateeResolvedPath, "array", length)

// find a unique index var name
let indexVarName = "__i"
Expand All @@ -299,7 +308,7 @@ ${options.contract ? "contract" : "library"} ${options.name || "Template"} {
scope.addVar(indexVarName)

const [itemVarAlias = "this", indexVarAlias = "@index"] =
statement.program.blockParams || []
program.blockParams || []

const newScope = scope.dive(`${iterateeResolvedPath}[${indexVarName}]`, {
[indexVarAlias]: indexVarName,
Expand All @@ -310,7 +319,7 @@ ${options.contract ? "contract" : "library"} ${options.name || "Template"} {
{
line: `for(uint256 ${indexVarName}; ${indexVarName} < ${iterateeResolvedPath}.length; ${indexVarName}++) {`,
},
...processProgram(statement.program, newScope),
...processProgram(program, newScope),
{
line: `}`,
},
Expand Down Expand Up @@ -447,7 +456,8 @@ class Scope {
function narrowInput(
inputType: InputType,
path: string, // example: __inputs.member[0].submember
narrowed: "string" | "array" | "bool" | "uint"
narrowed: "string" | "array" | "bool" | "uint" | "int",
length?: number
) {
let type = inputType
const [prefix, ...parts] = path.split(/\.|(?=\[)/g) // split at . and before [
Expand Down Expand Up @@ -519,6 +529,16 @@ function narrowInput(
type: narrowed,
})
}

if (length) {
if (!["string", "uint", "int", "array"].includes(narrowed)) {
throw new Error(`${narrowed} type does not have length`)
}
Object.assign(type, {
type: narrowed,
length: Math.max(("length" in type && type.length) || 0, length),
})
}
}

const resolveType = (inputType: InputType, path: string): InputType => {
Expand Down Expand Up @@ -601,21 +621,22 @@ const generateTypeNames = (
if (memberType.elementType.type === "array") {
throw new Error("Multi-dimensional arrays are not supported in ABI")
}
const length = memberType.length || ""

if (memberType.elementType.type === "struct") {
const structName = useExistingOrAddStruct(
singularize(name),
memberType.elementType
)
result.push({
name: `${structName}[]`,
name: `${structName}[${length}]`,
inputType: memberType,
})
generateTypeNames(memberType.elementType, result)
} else {
const elementTypeName = memberType.elementType.type || "string"
result.push({
name: `${elementTypeName}[]`,
name: `${elementTypeName}[${length}]`,
inputType: memberType,
})
}
Expand All @@ -626,6 +647,12 @@ const generateTypeNames = (
inputType: memberType,
})
generateTypeNames(memberType, result)
} else if ("length" in memberType && memberType.length) {
const typeName = memberType.type === "string" ? "bytes" : memberType.type
result.push({
name: `${typeName}${memberType.length}`,
inputType: memberType,
})
} else {
result.push({
name: memberType.type || "string",
Expand Down Expand Up @@ -676,24 +703,30 @@ const solDefinePartial = (partial: Partial, typeNames: TypeName[]) => {
`
}

const SOL_UINT2STR = `function uint2str(uint _i) internal pure returns (string memory) {
if (_i == 0) {
return "0";
const SOL_INT_TO_STRING = `function intToString(int256 i) internal pure returns (string memory) {
if (i >= 0) {
return uintToString(uint256(i));
}
return string(abi.encodePacked("-", uintToString(uint256(-i))));
}
function uintToString(uint256 i) internal pure returns (string memory) {
if (i == 0) {
return "0";
}
uint j = _i;
uint len;
uint256 j = i;
uint256 len;
while (j != 0) {
len++;
j /= 10;
len++;
j /= 10;
}
bytes memory bstr = new bytes(len);
uint k = len;
while (_i != 0) {
k = k-1;
uint8 temp = (48 + uint8(_i - _i / 10 * 10));
bytes1 b1 = bytes1(temp);
bstr[k] = b1;
_i /= 10;
uint256 k = len;
while (i != 0) {
k -= 1;
uint8 temp = (48 + uint8(i - (i / 10) * 10));
bstr[k] = bytes1(temp);
i /= 10;
}
return string(bstr);
}`
Expand Down
2 changes: 1 addition & 1 deletion test/cases/halo/template.svg.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<g transform="translate(1000 1060)">
{{#each rhythm}}
<g
style="transform: rotate(calc({{uint2str
style="transform: rotate(calc({{uint
@index
}} * 15deg)) translateY(-520px);"
>
Expand Down
59 changes: 59 additions & 0 deletions test/cases/wand.only/0.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
const a = {
title: "FLOURISHING MISTY WORLD",
background: { bg0: true },
planets: [
{ x: "-114", y: " 370" },
{ x: "-225", y: " 334" },
{ x: "-227", y: " 379" },
{ x: " 121", y: " 295" },
{ x: "-019", y: " 357" },
{ x: " 361", y: " 013" },
{ x: " 176", y: " 259" },
{ x: "-156", y: " 388" },
],
aspects: [
{ x1: " 259", y1: " 026", x2: "-091", y2: "-244" },
{ x1: " 259", y1: " 026", x2: "-259", y2: "-026" },
{ x1: " 083", y1: " 247", x2: " 084", y2: " 246" },
{ x1: " 083", y1: " 247", x2: " 258", y2: " 034" },
{ x1: " 087", y1: " 245", x2: " 258", y2: " 030" },
{ x1: " 248", y1: " 077", x2: " 191", y2: "-177" },
],
starsSeed: "5",
stone: {
seed: "1",
color: "crimson",
seasonsAmplitude: 260,
northernHemisphere: true,
secondInYear: 15389663,
secondInDay: 43200,
},
halo: {
rhythm: [
{ halo0: true },
{ halo1: true },
{ halo0: true },
{ halo1: true },
{ halo0: true },
{ halo1: true },
{ halo0: true },
{ halo1: true },
{ halo0: true },
{ halo1: true },
{ halo0: true },
{ halo1: true },
{ halo0: true },
{ halo1: true },
{ halo0: true },
{ halo1: true },
{ halo0: true },
{ halo1: true },
{ halo0: true },
{ halo1: true },
{ halo0: true },
{ halo1: true },
{ halo0: true },
{ halo1: true },
],
},
}
28 changes: 14 additions & 14 deletions test/cases/wand.only/0.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@
"title": "FLOURISHING MISTY WORLD",
"background": { "bg0": true },
"planets": [
{ "x": "-114", "y": " 370" },
{ "x": "-225", "y": " 334" },
{ "x": "-227", "y": " 379" },
{ "x": " 121", "y": " 295" },
{ "x": "-019", "y": " 357" },
{ "x": " 361", "y": " 013" },
{ "x": " 176", "y": " 259" },
{ "x": "-156", "y": " 388" }
{ "x": -114, "y": 370 },
{ "x": -225, "y": 334 },
{ "x": -227, "y": 379 },
{ "x": 121, "y": 295 },
{ "x": -19, "y": 357 },
{ "x": 361, "y": 13 },
{ "x": 176, "y": 259 },
{ "x": -156, "y": 388 }
],
"aspects": [
{ "x1": " 259", "y1": " 026", "x2": "-091", "y2": "-244" },
{ "x1": " 259", "y1": " 026", "x2": "-259", "y2": "-026" },
{ "x1": " 083", "y1": " 247", "x2": " 084", "y2": " 246" },
{ "x1": " 083", "y1": " 247", "x2": " 258", "y2": " 034" },
{ "x1": " 087", "y1": " 245", "x2": " 258", "y2": " 030" },
{ "x1": " 248", "y1": " 077", "x2": " 191", "y2": "-177" }
{ "x1": 259, "y1": 26, "x2": -91, "y2": -244 },
{ "x1": 259, "y1": 26, "x2": -259, "y2": -26 },
{ "x1": 83, "y1": 247, "x2": 84, "y2": 246 },
{ "x1": 83, "y1": 247, "x2": 258, "y2": 34 },
{ "x1": 87, "y1": 245, "x2": 258, "y2": 30 },
{ "x1": 248, "y1": 77, "x2": 191, "y2": -177 }
],
"starsSeed": "5",
"stone": {
Expand Down
Loading

0 comments on commit 18b657d

Please sign in to comment.