自分のPCからサーバにあるPCに対してメッセージを送信し、サーバのデータを使用してデータを取得できるとよさそうな気がした
書いているうちにanyがめちゃくちゃ多くなってしまった
参考
やりたいこと
クラウドデータに対して、データを取得することを考える
とりたいデータは以下のような感じ
計算式に渡すテキスト電文は以下を想定
ソースコード
import { Chunk, Effect, Either, Exit, ParseResult, Schema, Sink, Stream } from "effect"
// #region --- token ---
const TokenType = Schema.Literal("演算子", "数字", "変数")
const TokenType_演算子 = Schema.Literal("+", "-", "+", "ー")
const decode_TokenType_演算子 = Schema.decodeUnknownEither(TokenType_演算子)
const TokenType_数字 = Schema.Literal(
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"0"
)
const decode_TokenType_数字 = Schema.decodeUnknownEither(TokenType_数字)
function get_TokenType_From_String(text: string) {
const result_0 = decode_TokenType_演算子(text)
if (Either.isRight(result_0)) return TokenType.literals["0"]
const result_1 = decode_TokenType_数字(text)
if (Either.isRight(result_1)) return TokenType.literals["1"]
return TokenType.literals["2"]
}
// #endregion
// #region --- token object ---
const OneToken = Schema.Struct({
character: Schema.String,
token_type: TokenType
})
const OneToken_From_String = Schema.transform(
Schema.String,
OneToken,
{
strict: true,
decode: (text) => ({
character: text,
token_type: get_TokenType_From_String(text)
}),
encode: (token) => token.character
}
)
const decode_OneToken = Schema.decodeSync(OneToken_From_String)
// #endregion
// #region --- token object array ---
const TokenArray_From_String = Schema.transform(
Schema.String,
Schema.Array(OneToken),
{
strict: true,
decode: (text) =>
Array.from(text).map((one_text) => {
const result = decode_OneToken(one_text)
return result
}),
encode: (array) => array.map((token) => token.character).join("")
}
)
const decode_TokenArray = Schema.decodeSync(TokenArray_From_String)
const encode_TokenArray = Schema.encodeUnknownSync(TokenArray_From_String)
// #endregion
// #region --- token object jagged array ---
const TokenJaggedArray_From_TokenArray = Schema.transform(
Schema.Array(OneToken),
Schema.Array(Schema.Array(OneToken)),
{
strict: true,
decode: (array) => {
const streamArray = Stream.fromIterable(array)
const scannedStream = streamArray.pipe(
Stream.scan(
[] as Array<typeof OneToken>,
(a: [] | Array<typeof OneToken>, b: any) => { // TODO: <= OneToken
if (a.length === 0) {
const c = []
c.push(b)
return c
} else {
const last: any = a[a.length - 1] // TODO: OneToken
if (last.token_type === TokenType.literals["0"]) {
return [b]
} else {
if (last.token_type === b.token_type) {
const c = [...a]
c.push(b)
return c
} else {
return [b]
}
}
}
}
)
)
const scannedChunk = Effect.runSync(Stream.run(
scannedStream,
Sink.collectAll()
))
const jaggedStream = Stream.fromIterable(scannedChunk).pipe(
Stream.runFold(
[] as any, // TODO: <= OneToken[]
(a, b) => {
if (a.length === 0) {
if (b.length === 0) {
return []
} else {
const c = []
c.push(b)
return c
}
} else {
const last = a[a.length - 1]
if (last[0].token_type === b[0].token_type) {
const c = [...a]
c[c.length - 1] = b
return c
} else {
const c = [...a]
c.push(b)
return c
}
}
}
)
)
const jaggedChunk = Effect.runSync(Stream.run(
jaggedStream,
Sink.collectAll()
))
const resultArray = Chunk.toArray(jaggedChunk)
return resultArray[0]
},
encode: (jagged_array) => jagged_array.flat()
}
)
const decode_TokenJaggedArray = Schema.decodeSync(TokenJaggedArray_From_TokenArray)
const encode_TokenJaggedArray = Schema.encodeUnknownSync(TokenJaggedArray_From_TokenArray)
// #endregion
// #region --- operand object array ---
const Operator_演算子 = Schema.Struct({
operator: Schema.String,
token_type: Schema.Literal(TokenType.literals["0"]),
token_array: Schema.Array(OneToken)
})
const Operand_数字 = Schema.Struct({
number: Schema.Number,
token_type: Schema.Literal(TokenType.literals["1"]),
token_array: Schema.Array(OneToken)
})
const Operand_変数 = Schema.Struct({
name: Schema.String,
token_type: Schema.Literal(TokenType.literals["2"]),
token_array: Schema.Array(OneToken)
})
const OpItem_From_TokenArray = Schema.transform(
Schema.Array(OneToken),
Schema.Union(Operator_演算子, Operand_数字, Operand_変数),
{
strict: true,
decode: (array) => {
switch (array[0].token_type) {
case TokenType.literals["0"]:
return {
operator: array[0].character,
token_type: array[0].token_type,
token_array: array
}
case TokenType.literals["1"]: {
const total = array.reduce((p, c, ci) => {
const digit = Math.pow(10, array.length - ci - 1)
const value = digit * Number(c.character)
return p + value
}, 0)
return {
number: total,
token_type: array[0].token_type,
token_array: array
}
}
case TokenType.literals["2"]: {
const text = array.reduce((p, c) => p + c.character, "")
return {
name: text,
token_type: array[0].token_type,
token_array: array
}
}
}
},
encode: (op_item) => op_item.token_array
}
)
const decode_OpItem = Schema.decodeSync(OpItem_From_TokenArray)
const encode_OpItem = Schema.encodeUnknown(OpItem_From_TokenArray)
const OpArray_From_TokenJaggedArray = Schema.transform(
Schema.Array(Schema.Array(OneToken)),
Schema.Array(Schema.Union(Operator_演算子, Operand_数字, Operand_変数)),
{
strict: true,
decode: (jagged_array) => {
const resultArray = jagged_array.map((array) => decode_OpItem(array))
return resultArray
},
encode: (op_array) => {
const result = op_array.map((op_item) => Effect.runSync(encode_OpItem(op_item)))
return result
}
}
)
const decode_OpArray = Schema.decodeSync(OpArray_From_TokenJaggedArray)
const encode_OpArray = Schema.encodeUnknownSync(OpArray_From_TokenJaggedArray)
// #endregion
// #region --- expression object array ---
interface interface_Expression_式 {
operator: typeof Operator_演算子.Type
left: interface_Expression_式 | typeof Operand_数字.Type | typeof Operand_変数.Type
right: interface_Expression_式 | typeof Operand_数字.Type | typeof Operand_変数.Type
Tag: string
}
const expressionType = Schema.Union(
Schema.suspend((): Schema.Schema<interface_Expression_式> => Expression_式),
Operand_数字,
Operand_変数
)
const Expression_式 = Schema.Struct({
operator: Operator_演算子,
left: expressionType,
right: expressionType,
Tag: Schema.String
})
const getOpArray = (expression: interface_Expression_式) => {
const left: any = "Tag" in expression["left"] ? getOpArray(expression["left"]) : [expression["left"]]
const right: any = "Tag" in expression["right"] ? getOpArray(expression["right"]) : [expression["right"]]
return [...left, expression["operator"], ...right]
}
const Expression_From_OpArray_中置記法 = Schema.transformOrFail(
Schema.Array(Schema.Union(Operator_演算子, Operand_数字, Operand_変数)),
Expression_式,
{
strict: true,
decode: (array, _, ast) => {
const streamArray = Stream.fromIterable(array)
const resultStreamEffect = streamArray.pipe(
Stream.runFoldEffect(
[] as any, // TODO: <= Expression_式[]
(a, b) =>
Effect.gen(function*() {
if (a.length === 0) {
if (b.token_type === TokenType.literals["0"]) {
return yield* ParseResult.fail(
new ParseResult.Type(ast, "演算子の位置が不正です - leftの位置にあります")
)
} else {
const c = []
c.push(b)
return yield* ParseResult.succeed(c)
}
} else if (a.length === 1) {
if (b.token_type === TokenType.literals["0"]) {
const c = [...a]
c.push(b)
return yield* ParseResult.succeed(c)
} else {
return yield* ParseResult.fail(
new ParseResult.Type(ast, "演算子以外の位置が不正です - centerの位置にあります")
)
}
} else if (a.length === 2) {
if (b.token_type === TokenType.literals["0"]) {
return yield* ParseResult.fail(
new ParseResult.Type(ast, "演算子の位置が不正です - rightの位置にあります")
)
} else {
const c = []
const e = {
operator: a[1],
left: a[0],
right: b,
Tag: "Expression_式"
}
c.push(e)
return yield* ParseResult.succeed(c)
}
} else {
return yield* ParseResult.fail(new ParseResult.Type(ast, "処理が間違っています"))
}
})
)
)
const result = Effect.runSyncExit(resultStreamEffect).pipe(
Exit.match({
onSuccess: (value) =>
value.length === 1
? ParseResult.succeed(value[0])
: ParseResult.fail(new ParseResult.Type(ast, "式が完了しませんでした")),
onFailure: (_) => ParseResult.fail(new ParseResult.Type(ast, "処理が間違っています"))
})
)
return result
},
encode: (expression) => {
const result = getOpArray(expression)
return ParseResult.succeed(result)
}
}
)
const decode_Expression_Array_中置記法 = Schema.decodeUnknownSync(Expression_From_OpArray_中置記法 as any) // TODO: <= ?
const encode_Expression_Array_中置記法 = Schema.encodeUnknownSync(Expression_From_OpArray_中置記法 as any) // TODO: <= ?
// #endregion
// #region --- get value ---
// type getValueType = (parameter: { [key: string]: number }) => number
const getFunction_FromOperand = (operand: typeof Operand_数字.Type | typeof Operand_変数.Type) => {
if ("number" in operand) {
return () => operand.number
} else if ("name" in operand) {
return (parameter: { [operand.name]: number }) => parameter[operand.name]
}
}
const getFunction_FromExpression = (expression: interface_Expression_式) => {
const leftFunction: any = "Tag" in expression.left // TODO: <= getValueType
? getFunction_FromExpression(expression.left) // TODO: <= getValueType
: getFunction_FromOperand(expression.left)
const rightFunction: any = "Tag" in expression.right
? getFunction_FromExpression(expression.right)
: getFunction_FromOperand(expression.right)
switch (expression.operator.operator) {
case TokenType_演算子.literals["0"]:
case TokenType_演算子.literals["2"]: {
return (parameter: { [key: string]: number }) => {
return leftFunction(parameter) + rightFunction(parameter)
}
}
case TokenType_演算子.literals["1"]:
case TokenType_演算子.literals["3"]: {
return (parameter: { [key: string]: number }) => {
return leftFunction(parameter) - rightFunction(parameter)
}
}
}
throw Error("演算子の計算で想定外の動作が発生しました")
}
// #endregion
// --- main decode ---
const text = "売上高-売上原価+123"
const data_2025 = {
"記録年": 2025,
"売上高": 111000,
"売上原価": 1234
}
Effect.runSync(Effect.log("--- main decode ---"))
const decode_result_1 = decode_TokenArray(text)
Effect.runSync(Effect.log(decode_result_1))
const decode_result_2 = decode_TokenJaggedArray(decode_result_1)
Effect.runSync(Effect.log(decode_result_2))
const decode_result_3 = decode_OpArray(decode_result_2)
Effect.runSync(Effect.log(decode_result_3))
const decode_result_4 = decode_Expression_Array_中置記法(decode_result_3)
Effect.runSync(Effect.log(decode_result_4))
const result_5_function = getFunction_FromExpression(decode_result_4 as any)
const result_6_value = result_5_function(data_2025)
Effect.runSync(Effect.log(result_6_value))
// --- main encode ---
const Operand_売上高: typeof Operand_変数.Type = {
name: "売上高",
token_type: TokenType.literals["2"],
token_array: [
{
character: "売",
token_type: TokenType.literals["2"]
},
{
character: "上",
token_type: TokenType.literals["2"]
},
{
character: "高",
token_type: TokenType.literals["2"]
}
]
}
const Operand_売上原価: typeof Operand_変数.Type = {
name: "売上原価",
token_type: TokenType.literals["2"],
token_array: [
{
character: "売",
token_type: TokenType.literals["2"]
},
{
character: "上",
token_type: TokenType.literals["2"]
},
{
character: "原",
token_type: TokenType.literals["2"]
},
{
character: "価",
token_type: TokenType.literals["2"]
}
]
}
const operator_add: typeof Operator_演算子.Type = {
operator: "+",
token_type: TokenType.literals["0"],
token_array: [
{
character: "+",
token_type: TokenType.literals["0"]
}
]
}
const operator_subtract: typeof Operator_演算子.Type = {
operator: "-",
token_type: TokenType.literals["0"],
token_array: [
{
character: "-",
token_type: TokenType.literals["0"]
}
]
}
const Tag = "Expression_式"
const left: interface_Expression_式 = {
operator: operator_subtract,
left: Operand_売上高,
right: Operand_売上原価,
Tag
}
const right: typeof Operand_数字.Type = {
number: 123,
token_type: TokenType.literals["1"],
token_array: [
{
character: "1",
token_type: TokenType.literals["1"]
},
{
character: "2",
token_type: TokenType.literals["1"]
},
{
character: "3",
token_type: TokenType.literals["1"]
}
]
}
const expression: interface_Expression_式 = {
operator: operator_add,
left,
right,
Tag
}
Effect.runSync(Effect.log("--- main encode ---"))
const encode_result_1 = encode_Expression_Array_中置記法(expression)
Effect.runSync(Effect.log(encode_result_1))
const encode_result_2 = encode_OpArray(encode_result_1)
Effect.runSync(Effect.log(encode_result_2))
const encode_result_3 = encode_TokenJaggedArray(encode_result_2)
Effect.runSync(Effect.log(encode_result_3))
const encode_result_4 = encode_TokenArray(encode_result_3)
Effect.runSync(Effect.log(encode_result_4))
実行結果
timestamp=2025-10-12T04:11:31.581Z level=INFO fiber=#0 message="--- main decode ---"
timestamp=2025-10-12T04:11:31.603Z level=INFO fiber=#1 message="[
{
\"character\": \"売\",
\"token_type\": \"変数\"
},
{
\"character\": \"上\",
\"token_type\": \"変数\"
},
{
\"character\": \"高\",
\"token_type\": \"変数\"
},
{
\"character\": \"-\",
\"token_type\": \"演算子\"
},
{
\"character\": \"売\",
\"token_type\": \"変数\"
},
{
\"character\": \"上\",
\"token_type\": \"変数\"
},
{
\"character\": \"原\",
\"token_type\": \"変数\"
},
{
\"character\": \"価\",
\"token_type\": \"変数\"
},
{
\"character\": \"+\",
\"token_type\": \"演算子\"
},
{
\"character\": \"1\",
\"token_type\": \"数字\"
},
{
\"character\": \"2\",
\"token_type\": \"数字\"
},
{
\"character\": \"3\",
\"token_type\": \"数字\"
}
]"
timestamp=2025-10-12T04:11:31.639Z level=INFO fiber=#7 message="[
[
{
\"character\": \"売\",
\"token_type\": \"変数\"
},
{
\"character\": \"上\",
\"token_type\": \"変数\"
},
{
\"character\": \"高\",
\"token_type\": \"変数\"
}
],
[
{
\"character\": \"-\",
\"token_type\": \"演算子\"
}
],
[
{
\"character\": \"売\",
\"token_type\": \"変数\"
},
{
\"character\": \"上\",
\"token_type\": \"変数\"
},
{
\"character\": \"原\",
\"token_type\": \"変数\"
},
{
\"character\": \"価\",
\"token_type\": \"変数\"
}
],
[
{
\"character\": \"+\",
\"token_type\": \"演算子\"
}
],
[
{
\"character\": \"1\",
\"token_type\": \"数字\"
},
{
\"character\": \"2\",
\"token_type\": \"数字\"
},
{
\"character\": \"3\",
\"token_type\": \"数字\"
}
]
]"
timestamp=2025-10-12T04:11:31.641Z level=INFO fiber=#8 message="[
{
\"name\": \"売上高\",
\"token_type\": \"変数\",
\"token_array\": [
{
\"character\": \"売\",
\"token_type\": \"変数\"
},
{
\"character\": \"上\",
\"token_type\": \"変数\"
},
{
\"character\": \"高\",
\"token_type\": \"変数\"
}
]
},
{
\"operator\": \"-\",
\"token_type\": \"演算子\",
\"token_array\": [
{
\"character\": \"-\",
\"token_type\": \"演算子\"
}
]
},
{
\"name\": \"売上原価\",
\"token_type\": \"変数\",
\"token_array\": [
{
\"character\": \"売\",
\"token_type\": \"変数\"
},
{
\"character\": \"上\",
\"token_type\": \"変数\"
},
{
\"character\": \"原\",
\"token_type\": \"変数\"
},
{
\"character\": \"価\",
\"token_type\": \"変数\"
}
]
},
{
\"operator\": \"+\",
\"token_type\": \"演算子\",
\"token_array\": [
{
\"character\": \"+\",
\"token_type\": \"演算子\"
}
]
},
{
\"number\": 123,
\"token_type\": \"数字\",
\"token_array\": [
{
\"character\": \"1\",
\"token_type\": \"数字\"
},
{
\"character\": \"2\",
\"token_type\": \"数字\"
},
{
\"character\": \"3\",
\"token_type\": \"数字\"
}
]
}
]"
timestamp=2025-10-12T04:11:31.647Z level=INFO fiber=#11 message="{
\"operator\": {
\"operator\": \"+\",
\"token_type\": \"演算子\",
\"token_array\": [
{
\"character\": \"+\",
\"token_type\": \"演算子\"
}
]
},
\"left\": {
\"operator\": {
\"operator\": \"-\",
\"token_type\": \"演算子\",
\"token_array\": [
{
\"character\": \"-\",
\"token_type\": \"演算子\"
}
]
},
\"left\": {
\"name\": \"売上高\",
\"token_type\": \"変数\",
\"token_array\": [
{
\"character\": \"売\",
\"token_type\": \"変数\"
},
{
\"character\": \"上\",
\"token_type\": \"変数\"
},
{
\"character\": \"高\",
\"token_type\": \"変数\"
}
]
},
\"right\": {
\"name\": \"売上原価\",
\"token_type\": \"変数\",
\"token_array\": [
{
\"character\": \"売\",
\"token_type\": \"変数\"
},
{
\"character\": \"上\",
\"token_type\": \"変数\"
},
{
\"character\": \"原\",
\"token_type\": \"変数\"
},
{
\"character\": \"価\",
\"token_type\": \"変数\"
}
]
},
\"Tag\": \"Expression_式\"
},
\"right\": {
\"number\": 123,
\"token_type\": \"数字\",
\"token_array\": [
{
\"character\": \"1\",
\"token_type\": \"数字\"
},
{
\"character\": \"2\",
\"token_type\": \"数字\"
},
{
\"character\": \"3\",
\"token_type\": \"数字\"
}
]
},
\"Tag\": \"Expression_式\"
}"
timestamp=2025-10-12T04:11:31.649Z level=INFO fiber=#12 message=109889
timestamp=2025-10-12T04:11:31.650Z level=INFO fiber=#13 message="--- main encode ---"
timestamp=2025-10-12T04:11:31.652Z level=INFO fiber=#14 message="[
{
\"name\": \"売上高\",
\"token_type\": \"変数\",
\"token_array\": [
{
\"character\": \"売\",
\"token_type\": \"変数\"
},
{
\"character\": \"上\",
\"token_type\": \"変数\"
},
{
\"character\": \"高\",
\"token_type\": \"変数\"
}
]
},
{
\"operator\": \"-\",
\"token_type\": \"演算子\",
\"token_array\": [
{
\"character\": \"-\",
\"token_type\": \"演算子\"
}
]
},
{
\"name\": \"売上原価\",
\"token_type\": \"変数\",
\"token_array\": [
{
\"character\": \"売\",
\"token_type\": \"変数\"
},
{
\"character\": \"上\",
\"token_type\": \"変数\"
},
{
\"character\": \"原\",
\"token_type\": \"変数\"
},
{
\"character\": \"価\",
\"token_type\": \"変数\"
}
]
},
{
\"operator\": \"+\",
\"token_type\": \"演算子\",
\"token_array\": [
{
\"character\": \"+\",
\"token_type\": \"演算子\"
}
]
},
{
\"number\": 123,
\"token_type\": \"数字\",
\"token_array\": [
{
\"character\": \"1\",
\"token_type\": \"数字\"
},
{
\"character\": \"2\",
\"token_type\": \"数字\"
},
{
\"character\": \"3\",
\"token_type\": \"数字\"
}
]
}
]"
timestamp=2025-10-12T04:11:31.654Z level=INFO fiber=#15 message="[
[
{
\"character\": \"売\",
\"token_type\": \"変数\"
},
{
\"character\": \"上\",
\"token_type\": \"変数\"
},
{
\"character\": \"高\",
\"token_type\": \"変数\"
}
],
[
{
\"character\": \"-\",
\"token_type\": \"演算子\"
}
],
[
{
\"character\": \"売\",
\"token_type\": \"変数\"
},
{
\"character\": \"上\",
\"token_type\": \"変数\"
},
{
\"character\": \"原\",
\"token_type\": \"変数\"
},
{
\"character\": \"価\",
\"token_type\": \"変数\"
}
],
[
{
\"character\": \"+\",
\"token_type\": \"演算子\"
}
],
[
{
\"character\": \"1\",
\"token_type\": \"数字\"
},
{
\"character\": \"2\",
\"token_type\": \"数字\"
},
{
\"character\": \"3\",
\"token_type\": \"数字\"
}
]
]"
timestamp=2025-10-12T04:11:31.656Z level=INFO fiber=#16 message="[
{
\"character\": \"売\",
\"token_type\": \"変数\"
},
{
\"character\": \"上\",
\"token_type\": \"変数\"
},
{
\"character\": \"高\",
\"token_type\": \"変数\"
},
{
\"character\": \"-\",
\"token_type\": \"演算子\"
},
{
\"character\": \"売\",
\"token_type\": \"変数\"
},
{
\"character\": \"上\",
\"token_type\": \"変数\"
},
{
\"character\": \"原\",
\"token_type\": \"変数\"
},
{
\"character\": \"価\",
\"token_type\": \"変数\"
},
{
\"character\": \"+\",
\"token_type\": \"演算子\"
},
{
\"character\": \"1\",
\"token_type\": \"数字\"
},
{
\"character\": \"2\",
\"token_type\": \"数字\"
},
{
\"character\": \"3\",
\"token_type\": \"数字\"
}
]"
timestamp=2025-10-12T04:11:31.657Z level=INFO fiber=#17 message=売上高-売上原価+123
計算指示から計算結果を取得するところ
decodeの個所がテキスト電文から計算オブジェクトを作成している
getFunction_FromExpression で 計算用の関数を生成し、
その関数にサーバデータをいれることで計算を実施している
const result_5_function = getFunction_FromExpression(decode_result_4 as any)
const result_6_value = result_5_function(data_2025)
Effect.runSync(Effect.log(result_6_value))