dak ブログ

python、rubyなどのプログラミング、MySQL、サーバーの設定などの備忘録。レゴの写真も。

TypeScript での catch と finally の処理順

2022-09-09 00:24:09 | Node.js
TypeScript で例外が発生した場合に catch と finally がどのような順に実行されるかを調べてみました。
以下のプログラムでは、func1() から func2() を呼び出し、func2() からはさらに func3() を呼び出します。
func3() 内で throw した場合に、catch と finally がどのような順で呼び出されるかを確認します。

■プログラム
function func1() {
  try {
    console.log("func1: start");
    func2();
  }
  catch (e) {
    console.log("func1: catch");
  }
  finally {
    console.log("func1: finally");
  }

  console.log("func1: end");
}

function func2() {
  try {
    console.log("func2: start");
    func3();
  }
  catch (e) {
    console.log("func2: catch");
    throw e;
  }
  finally {
    console.log("func2: finally");
  }

  console.log("func2: end");
}

function func3() {
  try {
    console.log("func3: start");
    throw 'func3';
  }
  catch (e) {
    console.log("func3: catch");
    throw e;
  }
  finally {
    console.log("func3: finally");
  }

  console.log("func3: end");
}

func1();

■実行結果
func1: start
func2: start
func3: start
func3: catch
func3: finally
func2: catch
func2: finally
func1: catch
func1: finally
func1: end


実行結果をみると、finally が定義されている場合には catch が実行された後に finally が呼び出されていますが、catch 内で throw した場合には呼び出し元の処理に戻る前に finally が実行されています。


Typescript で id を生成

2022-09-03 00:00:45 | Node.js
Typescript でランダムな id を生成する方法のメモ。
uuid を利用します。
■インストール
npm install uuid
npm i --save-dev @types/uuid

■プログラム
import { v4 as uuidv4 } from 'uuid';
const id = uuidv4();
console.log(id);

■実行結果
342f3d57-e1e0-45c0-a19f-fe5108eb823b


jsdom で属性を取得

2022-08-26 23:06:42 | Node.js
jsdom で属性を取得する方法のメモ。
getAttributeNames() で属性名のリストを取得できます。
そして、getAttribute(属性名) で属性値を取得することができます。
const { JSDOM } = require('jsdom');

const html = `
<html>
<head>
<title>test</title>
<meta keyword="kw1,kw2,kw3">
</head>
<body>
abc
<img src="image1.png" alt="image1"/>
<div>div1-1</div>
xyz
</body>
</html>
`;

const dom = new JSDOM(html);
const doc = dom.window.document;

function get_texts(node, texts=[]) {
  console.log("--");
  console.log("nodeType: " + node.nodeType);
  console.log("nodeName: " + node.nodeName);
  console.log("nodeValue: " + node.nodeValue);

  if (node.nodeType === node.TEXT_NODE) {
    texts.push(node.nodeValue);
  }
  else if (node.nodeType === node.ELEMENT_NODE) {
    console.log("attrs: " + node.getAttributeNames());
    console.log("alt: " + node.getAttribute('alt'));

    for (const child of node.childNodes) {
      get_texts(child, texts);
    }
  }

  return texts;
}

const texts = get_texts(doc.body);
console.log(texts);

■実行結果
--
nodeType: 1
nodeName: BODY
nodeValue: null
attrs:
alt: null
--
nodeType: 3
nodeName: #text
nodeValue:
abc

--
nodeType: 1
nodeName: IMG
nodeValue: null
attrs: src,alt
alt: image1
--
nodeType: 3
nodeName: #text
nodeValue:

--
nodeType: 1
nodeName: DIV
nodeValue: null
attrs:
alt: null
--
nodeType: 3
nodeName: #text
nodeValue: div1-1
--
nodeType: 3
nodeName: #text
nodeValue:
xyz


Node.js での jsdom を利用した HTML からのテキスト抽出

2022-08-20 16:16:19 | Node.js
Node.js で jsdom を利用して HTML からテキストを抽出する方法のメモ。
dom = new JSDOM(...) で DOM を生成すると、dom.window.document で HTML の DOM ツリーを参照できます。
nodeType が TEXT_NODE の場合に、nodeValue に格納されているテキストを抽出します。

■プログラム
const jsdom = require('jsdom');

// 以下が対象の HTML
const html = `
<html>
<head>
<title>test</title>
<meta keyword="kw1, kw2, kw3">
</head>
<body>
abc
<div>div1-1</div>
<div>div2-1<div>div2_2-1</div>div2-2</div>
<div>div3-1</div>
xyz
</body>
</html>
`;

// DOM 生成
const dom = new jsdom.JSDOM(html);

// 再帰的に DOM ツリーをたどってテキストを抽出
function get_texts(node, texts=[]) {
  if (node.nodeType === node.TEXT_NODE) {
    texts.push(node.nodeValue);
  }
  else if (node.nodeType === node.ELEMENT_NODE) {
    for (const child of node.childNodes) {
      get_texts(child, texts);
    }
  }

  return texts;
}

const doc = dom.window.document;
const texts = get_texts(doc.body);
console.log(texts);

■実行結果
[
  '\nabc\n',
  'div1-1',
  '\n',
  'div2-1',
  'div2_2-1',
  'div2-2',
  '\n',
  'div3-1',
  '\nxyz\n\n\n'
]

pythonia による node.js からの python の関数の実行

2022-04-16 15:19:27 | Node.js
pythonia で使うことで、node.js (TypeScript) から python の関数を実行することができます。

■python プログラム (list.py)
def list_sum(lst):
    s = 0
    for val in lst:
        s += val
    return s

def list_mul(lst):
    m = 0
    for i, val in enumerate(lst):
        if i == 0:
            m = val
        else:
            m *= val
    return m

■TypeScript プログラム
import process from 'process';
import { python } from 'pythonia';

(async () => {
  const list = await python('./list.py'); // ./ が必要
  const lst = [1, 2, 4, 6, 8];

  const s = await list.list_sum(lst);
  console.log(s);

  const m = await list.list_mul(lst);
  console.log(m);

  process.exit(0);
})();

■実行結果
加算、乗算の計算結果が得られます。
21
384

ただし、以下のエラーメッセージが出力されます。
Exception ignored in: <function Proxy.__del__ at 0x7f404f6e7f70>
...
SystemExit: 1

node.js からの python プログラム実行

2022-04-16 12:24:25 | Node.js
Python-Shell を使って、node.js (TypeScript) から python のプログラムを実行する方法のメモ。

Python-Shell では node.js から python のメソッドを直接呼び出すわけではなく、
別プロセスで python プログラムを実行し、標準入出力で python プログラムとやりとりします。

今回は、以下のようなプログラムを作成してみました。
・python プログラム
 標準入力から1行読み込み、空白区切りで行を分割し、合計値を計算します。
 例: "1 2 3" => 6

・TypeScript プログラム
 空白区切りの数字文字列を python プログラムに送り、結果を取得して表示します。

■python プログラム
行毎に flush しないと、python 側で無限に結果待ちの状態となります。
import sys
import re
import json

def proc_record(res, line):
    line = re.sub('[\r\n]+$', '', line)
    items = re.split('[ ]+', line)
    res['result'] = sum([int(item) for item in items])

def main():
    for line in sys.stdin:
        res = {
            'status': 0,
            'message': 'OK',
            'result': None,
        }

        try:
            proc_record(res, line)
        except Exception as e:
            res['status'] = 1
            res['message'] = 'error'
        finally:
            print(json.dumps(res, ensure_ascii=False))
            sys.stdout.flush()

    return 0

if __name__ == '__main__':
    res = main()
    exit(res)

■TypeScript プログラム
import { PythonShell } from 'python-shell';

async function recv(pysh: PythonShell): Promise<any> {
  return new Promise((resolve) => {
    pysh.on('message', (msg) => {
      resolve(msg);
    });
  });
}

(async () => {
  const pysh = new PythonShell('sum.py');

  pysh.send('1 2 3');
  const res1 = await recv(pysh);
  console.log(res1);

  pysh.send('1 3 5');
  const res2 = await recv(pysh);
  console.log(res2);

  pysh.end(() => {});
})();

■実行結果
{"status": 0, "message": "OK", "result": 6}
{"status": 0, "message": "OK", "result": 9}


cluster によるマルチプロセスでの express の http サーバ

2022-04-09 14:15:22 | Node.js
cluster を使うと、マルチプロセスで処理を行うことができます。
cluster では、親となる master が fork() して子の worker を生成します。

以下では、woker が http リクエストを処理し、master は worker が異常終了した場合に、
再度 worker を生成するようにしています。
import os from 'os';
import process from 'process';
import cluster from 'cluster';
import express from 'express';

const port = 8103;
const pid = process.pid;
console.log(`pid: ${pid}`);

if (cluster.isMaster) {
  console.log(`master: ${pid}`);
  const num_cpus = os.cpus().length;
  for (let i = 0; i < num_cpus * 3; i++) { // CPU がひとつのため x 3 している
    cluster.fork();
  }

  cluster.on('exit', (worker, code, signal) => {
    const wpid = worker.process.pid;
    console.log(`exit: ${wpid}`);
    cluster.fork();
  });
}
else {
  const app = express();

  app.get('/', (http_req, http_res) => {
    console.log(`receive: ${pid}`);
    http_res.send(`worker_pid: ${pid}\n`);
  });

  app.listen(port, () => {
    console.log(`http server start: ${pid}`);
  });
}

■実行例
最初にプロセスIDを出力するようにしているため、master のプロセスIDが出力されます。
pid: 9394
master: 9394

続いて、worker のプロセスID が出力され、その後 http サーバの初期化が行われます。
pid: 9401
pid: 9402
pid: 9403
http server start: 9401
http server start: 9402
http server start: 9403

■http リクエスト送信
http リクエストを何度か送信すると、http クライアントにはリクエストを処理した worker のプロセスIDが返却されます。
curl 'http://localhost:8103/'
-->
pid: 9403

curl 'http://localhost:8103/'
-->
pid: 9402

curl 'http://localhost:8103/'
-->
pid: 9401

■worker を kill
pid=9401 の worker を kill します。
kill -9 9401

すると、master が新たに worker を生成します。
exit: 9401
pid: 9430
http server start: 9430

http リクエストを送信すると、新たな worker もレスポンスを返します。
curl 'http://localhost:8103/'
-->
pid: 9430

■master を kill
master を kill すると、worker も終了します。
kill -9 9394
ps
    PID TTY          TIME CMD
   5923 pts/1    00:00:00 bash
   9565 pts/1    00:00:00 ps




node.js 版の kuromoji での形態素解析

2022-04-08 23:37:06 | Node.js
node.js 版の kuromoji で形態素解析をしてみました。
今回は TypeScript で kuromoji を使います。

kuromoji のインストール方法は以下の通りです。
npm install kuromoji
npm install @types/kuromoji


kuromoji を使いやすくするため、以下のようにライブラリ化しました。
■KuromojiUtil.ts
/**
 * Kuromoji Util
 */

import kuromoji, { Tokenizer, IpadicFeatures, TokenizerBuilder } from 'kuromoji';

export default class KuromojiUtil {
  private constructor() {}

  public static getTokenizer(paramh: any): Promise<Tokenizer<IpadicFeatures>> {
    const builder: TokenizerBuilder<IpadicFeatures> =
      kuromoji.builder(paramh);

    return new Promise<Tokenizer<IpadicFeatures>>(done => {
      builder.build((err, tknzr) => {
        done(tknzr);
      });
    });
  }
}

上記の KuromojiUtil.ts を使って形態素解析を行います。
import KuromojiUtil from './KuromojiUtil';

(async () => {
  const paramh = {
    dicPath: 'node_modules/kuromoji/dict',
  };

  const tknzr = await KuromojiUtil.getTokenizer(paramh);
  const text = '日本語の文です';
  const tkns = tknzr.tokenize(text);
  console.log(tkns);
})();

■実行結果
[
  {
    word_id: 2591070,
    word_type: 'KNOWN',
    word_position: 1,
    surface_form: '日本語',
    pos: '名詞',
    pos_detail_1: '一般',
    pos_detail_2: '*',
    pos_detail_3: '*',
    conjugated_type: '*',
    conjugated_form: '*',
    basic_form: '日本語',
    reading: 'ニホンゴ',
    pronunciation: 'ニホンゴ'
  },
  {
    word_id: 93100,
    word_type: 'KNOWN',
    word_position: 4,
    surface_form: 'の',
    pos: '助詞',
    pos_detail_1: '連体化',
    pos_detail_2: '*',
    pos_detail_3: '*',
    conjugated_type: '*',
    conjugated_form: '*',
    basic_form: 'の',
    reading: 'ノ',
    pronunciation: 'ノ'
  },
  {
    word_id: 2475380,
    word_type: 'KNOWN',
    word_position: 5,
    surface_form: '文',
    pos: '名詞',
    pos_detail_1: '一般',
    pos_detail_2: '*',
    pos_detail_3: '*',
    conjugated_type: '*',
    conjugated_form: '*',
    basic_form: '文',
    reading: 'ブン',
    pronunciation: 'ブン'
  },
  {
    word_id: 23760,
    word_type: 'KNOWN',
    word_position: 6,
    surface_form: 'です',
    pos: '助動詞',
    pos_detail_1: '*',
    pos_detail_2: '*',
    pos_detail_3: '*',
    conjugated_type: '特殊・デス',
    conjugated_form: '基本形',
    basic_form: 'です',
    reading: 'デス',
    pronunciation: 'デス'
  }
]


chevrotain で簡易な正規表現の構文解析

2022-04-03 17:51:00 | Node.js
chevrotain での簡易な正規表現の構文解析のメモ。
以下のような簡易な正規表現の構文解析を行います。
root -> exprs
exprs -> expr+
expr -> or_expr
or_expr -> '(' str or_strs ')' '?'?
or_expr -> str
or_strs -> ('|' or_str)+
str -> STR

上記の文法は、以下のような文字列を受理します。
abc
(abc|def)
(abc|def)?
(abc|def)(ghi|jkl|mno)?(pqr|stu)

プログラムは以下の通りです。
import { CstParser, Lexer, createToken, Rule } from 'chevrotain'

// lexer
const STR = createToken({ name: "STR", pattern: /[^()|?]+/ });
const LP = createToken({ name: "LP", pattern: /[(]/ });
const RP = createToken({ name: "RP", pattern: /[)]/ });
const PIPE = createToken({ name: "PIPE", pattern: /[|]/ });
const QM = createToken({ name: "QM", pattern: /[?]/ });

const allTokens = [
  STR,
  LP,
  RP,
  PIPE,
  QM,
];

const lexer = new Lexer(allTokens, { positionTracking: "onlyOffset" });
  

// parser
class MatchOrParser extends CstParser {
  public value_stack: any[] = [];
  
  constructor() {
    super(allTokens);
    this.performSelfAnalysis();
  }
  
  public root = this.RULE("root", () => {
    this.SUBRULE1(this.exprs);
  });
  
  public exprs = this.RULE("exprs", () => {
    this.SUBRULE1(this.expr);
    this.MANY(() => {
      this.SUBRULE2(this.expr);
    });
  });
  
  public expr = this.RULE("expr", () => {
    this.SUBRULE(this.or_expr);
  });
  
  public or_expr = this.RULE("or_expr", () => {
    this.OR([
      { ALT: () => {
	this.CONSUME(LP);
	this.SUBRULE1(this.str);
	this.SUBRULE2(this.or_strs);
	this.CONSUME(RP);
	this.OPTION(() => { this.CONSUME(QM); });
      }},
      { ALT: () => {
	this.SUBRULE3(this.str);
      }},
    ]);
  });
  
  public or_strs = this.RULE("or_strs", () => {
    this.MANY(() => {
      this.CONSUME(PIPE);
      this.SUBRULE(this.str);
    });
  });
  
  public str = this.RULE("str", () => {
    this.CONSUME(STR);
  });
}


const parser = new MatchOrParser();
const BaseCstVisitor = parser.getBaseCstVisitorConstructor();


class MatchOrVisitor extends BaseCstVisitor {
  public constructor() {
    super();
    this.validateVisitor();
  }

  root(ctx: any) {
    const v = this.visit(ctx.exprs);
    return {
      type: "root",
      exprs: v.exprs,
    };
  }
  
  exprs(ctx: any) {
    const ret = {
      type: "exprs",
      exprs: [],
    };
    
    for (let e of ctx.expr) {
      let v: any = this.visit(e);
      //console.log(JSON.stringify(v));
      ret.exprs.push(v as never);
    }

    return ret;
  }
  
  expr(ctx: any) {
    if (ctx.or_expr) {
      const v1 = this.visit(ctx.or_expr);
      return v1;
    }
    else {
      const v2 = this.visit(ctx.str);
      return v2;
    }
  }
  
  or_expr(ctx: any) {
    if (ctx.or_strs) {
      const v1 = this.visit(ctx.str);
      const v2 = this.visit(ctx.or_strs);
      return {
	type: "or_expr",
	exprs: [v1.str].concat(v2.strs),
	qm: ctx.QM ? true : false,
      };
    }
    else {
      const v = this.visit(ctx.str);
      return {
	type: "str",
	str: v.str,
      };
    }
  }

  or_strs(ctx: any) {
    const ret = {
      type: "or_strs",
      strs: [],
    };

    for (let e of ctx.str) {
      let v = this.visit(ctx.str);
      ret.strs.push(v.str as never);
    }

    return ret;
  }

  str(ctx: any) {
    return {
      type: "str",
      str: ctx.STR[0].image,
    };
  }
}


const visitor = new MatchOrVisitor();

const texts = [
  'あ',
  '(あ|ア)',
  '(あ|ア)?',
  '(あい|アイ)(う|ウ)?(えお|エオ)',
];

for (let text of texts) {
  console.log(text);
  
  let lex_result = lexer.tokenize(text);
  parser.input = lex_result.tokens;
  let cst = parser.root();
  //console.log(JSON.stringify(cst));
  let res = visitor.visit(cst);
  console.log(JSON.stringify(res));
}

■実行結果
あ
{"type":"root","exprs":[{"type":"str","str":"あ"}]}

(あ|ア)
{"type":"root","exprs":[{"type":"or_expr","exprs":["あ","ア"],"qm":false}]}

(あ|ア)?
{"type":"root","exprs":[{"type":"or_expr","exprs":["あ","ア"],"qm":true}]}

(あい|アイ)(う|ウ)?(えお|エオ)
{"type":"root","exprs":[{"type":"or_expr","exprs":["あい","アイ"],"qm":false},{"type":"or_expr","exprs":["う","ウ"],"qm":true},{"type":"or_expr","exprs":["えお","エオ"],"qm":false}]}


chevrotain での加減算の構文解析結果を visitor で実行

2022-04-03 13:51:47 | Node.js
chevrotain での加減算の構文解析結果を visitor で実行する方法のメモ。

ここでは、以下の加減算の文法を対象とした構文解析器を作成します。
calc -> expr
expr -> val ('+' | '-') val
val -> [0-9]+

lexer で字句解析を行い、parser で構文解析を行います。
そして、visitor で構文解析結果を評価し、演算結果を取得します。

プログラムは以下の通りです。
import { CstParser, Lexer, createToken, Rule } from 'chevrotain'

/**
 * lexer
 */
const Num = createToken({ name: "Num", pattern: /[0-9]+/ });
const Plus = createToken({ name: "Plus", pattern: /[+]/ });
const Minus = createToken({ name: "Minus", pattern: /[-]/ });

const allTokens = [
  Num,
  Plus,
  Minus,
];

const calcLexer = new Lexer(allTokens, { positionTracking: "onlyOffset" });

/**
 * parser
 */
class CalcParser extends CstParser {
  public value_stack: any[] = [];
  
  constructor() {
    super(allTokens);
    this.performSelfAnalysis();
  }
  
  public calc = this.RULE("calc", () => {
    this.SUBRULE(this.expr);
  });
 
  public expr = this.RULE("expr", () => {
    this.SUBRULE1(this.val);
    this.OR([
      { ALT: () => { this.CONSUME1(Plus); }},
      { ALT: () => { this.CONSUME2(Minus); }},
    ]);
    this.SUBRULE2(this.val);
  });

  private val = this.RULE("val", () => {
    this.CONSUME(Num);
  });
}

const parser = new CalcParser();
const BaseCstVisitor = parser.getBaseCstVisitorConstructor();

/**
 * visitor
 */
class CalcVisitor extends BaseCstVisitor {
  public constructor() {
    super();
    this.validateVisitor();
  }

  public calc(ctx: any) {
    const v = this.visit(ctx.expr);
    return {
      type: "calc",
      value: v.value,
    };
  }
  
  public expr(ctx: any) {
    const v_val1 = this.visit(ctx.val[0]);
    const v_val2 = this.visit(ctx.val[1]);
    if (ctx.Plus) {
      return {
	type: "expr",
	value: v_val1.value + v_val2.value,
      };
    }
    else {
      return {
	type: "expr",
	value: v_val1.value - v_val2.value,
      };
    };
  }

  public val(ctx: any) {
    const value = parseInt(ctx.Num[0].image);
    return {
      type: "val",
      value: value,
    };
  }
}

const calc_visitor = new CalcVisitor();

const texts = [
  '1+2',
  '7-3',
  '3*5', // 対象外の文法
];

for (let text of texts) {
  console.log(text);
  
  let lex_result = calcLexer.tokenize(text);
  parser.input = lex_result.tokens;
  let cst = parser.calc();
  let res = calc_visitor.visit(cst);
  console.log(res);
}

■実行結果
1+2
3
7-3
4
3*5
undefined

対象外の文法の場合には、undefined が返却されます。


TypeScriptでライブラリのパスに @ を使えるようにする方法

2022-03-17 23:46:25 | Node.js
TypeScriptでライブラリのパスに @ を使えるようにする方法のメモ。
tsconfig.json で以下のように paths を設定します。
{
  "compilerOption": {
    ...,
    "paths": {
      "@/*": ["./*"],
    },
    ...
}

ts-node で実行する ts ファイルを指定する際にコマンドラインオプションを指定します。
ts-node -r tsconfig-paths/register {TypeScriptファイル.ts}


Node.js での map() による Array 処理

2022-02-26 23:28:42 | Node.js
Node.js で map() で Array 処理を行う方法のメモ。
const str_objs = [{str: 'abc'}, {str: 'def'}, {str: 'ghi'}];
const strs = str_objs.map((x) => { return x.str; });
console.log(strs);

■実行結果
[ 'abc', 'def', 'ghi' ]


Typescript で pdf ファイルのテキストを抽出

2022-01-20 23:30:02 | Node.js
Typescript で pdf ファイルのテキストを抽出する方法のメモ。
import * as pdfjs from 'pdfjs-dist/legacy/build/pdf';

function main() {
  const pdf_file: string = process.argv[2];

  pdfjs.getDocument(pdf_file).promise.then((pdf: pdfjs.PDFDocumentProxy) => {
    console.log('pages: ' + pdf.numPages);

    for (let p = 1; p <= pdf.numPages; p++) {
      pdf.getPage(p).then((page) => {
        page.getTextContent().then((text_content) => {
          for (let i = 0; i < text_content.items.length; i++) {
            let item: any = text_content.items[i];
            let str = item.str;
            str = str.replace(/[\r\n]+/g, '');
            if (str.match(/^\s*$/)) continue;
            console.log(item.str);
          }
        });
      });
    }
  });
}

main();


Typescript で pdf ファイルのページ数を取得

2022-01-16 23:23:19 | Node.js
Typescript で pdf.js で pdf ファイルのページ数を取得する方法のメモ。
import * as pdfjs from 'pdfjs-dist/legacy/build/pdf';

function main() {
  const pdf_file = process.argv[2];

  pdfjs.getDocument(pdf_file).promise.then((pdf: pdfjs.PDFDocumentProxy) => {
    console.log('pages: ' + pdf.numPages);
  });
}

main();

ts-node での実行例
ts-node {プログラム.ts} {pdfファイル.pdf}


Typescript で MeCab で形態素解析

2022-01-13 21:04:58 | Node.js
Typescript で MeCab で形態素解析する方法のメモ。
TypeScript で MeCab を実行するのに mecab-client を使用します。

■MeCab のインストール
yum install mecab mecab-ipadic

■mecab-client のインストール
npm install mecab-client

■mecab-client を利用したサンプルプログラム
import { MeCab } from 'mecab-client';

async function main() {
  const tknzr = new MeCab();
  const str = '私は人間です。';

  const tkns = await tknzr.parse(str);
  for (let tkn of tkns) {
    console.log(tkn);
  }
}

main();

■実行結果
解析結果は token のリストで返却されます。
{ surface: '私',
  lexical: '名詞',
  compound1: '代名詞',
  compound2: '一般',
  compound3: '*',
  conjugation: '*',
  inflection: '*',
  original: '私',
  reading: 'ワタシ',
  pronunciation: 'ワタシ' }
{ surface: 'は',
  lexical: '助詞',
  compound1: '係助詞',
  compound2: '*',
  compound3: '*',
  conjugation: '*',
  inflection: '*',
  original: 'は',
  reading: 'ハ',
  pronunciation: 'ワ' }
{ surface: '人間',
  lexical: '名詞',
  compound1: '一般',
  compound2: '*',
  compound3: '*',
  conjugation: '*',
  inflection: '*',
  original: '人間',
  reading: 'ニンゲン',
  pronunciation: 'ニンゲン' }
{ surface: 'です',
  lexical: '助動詞',
  compound1: '*',
  compound2: '*',
  compound3: '*',
  conjugation: '特殊・デス',
  inflection: '基本形',
  original: 'です',
  reading: 'デス',
  pronunciation: 'デス' }
{ surface: '。',
  lexical: '記号',
  compound1: '句点',
  compound2: '*',
  compound3: '*',
  conjugation: '*',
  inflection: '*',
  original: '。',
  reading: '。',
  pronunciation: '。' }