ãã¸ã«ã«éçºã¡ã³ãã¼ã®æ«æ°¸(@asmsuechan)ã§ããé»åã«ã«ãã®éçºãéãã¦å»å¸«ãå»çäºåã®æ¹ã®ãæ¸ããããµãã¼ããã¦ãã¾ãã
ã¿ãªããããã¼ã¯ãã¦ã³ã§ããã¹ããæ¸ãã¦ãã¾ããï¼ç§ã¯ãã¼ã¯ãã¦ã³ã§æ¸ããã¨ã好ãã§ããã¬ã¼ã³ããã¹ãã§ãã»ã¨ãã©ã®ã¡ã¢ããã¼ã¯ãã¦ã³è¨æ³ã§æ¸ãã¦ãã¾ãã¾ãããã®è¨äºãã¯ã¦ãªã®ãã¼ã¯ãã¦ã³ã使ã£ã¦æ¸ãã¦ãã¾ãã*1
ãã¼ã¯ãã¦ã³ã使ãããµã¼ãã¹ãæãã¦ããã¨GitHubãStackOverflowãªã©éçºè ãªãã°èª°ããç¥ããµã¼ãã¹ãå¤ãåºã¦ãã¾ãããã®ãã¨ããããããããã«ããã¼ã¯ãã¦ã³ã¯ãã¯ãéçºè ã¨ã¯ç¸ã®åããªããã®ã¨ãªã£ã¦ãã¾ãã
ããããæ®æ®µã®éçºæã«ãã使ããã¼ã¯ãã¦ã³ã§ãããå®éã«ã¯ã©ã®ãããªä»çµã¿ã§åãã¦ããã®ããç¥ã人ã¯å°ãªãã®ã§ã¯ãªããã¨æãã¾ãã
ãã®è¨äºã§ã¯ãªãªã¸ãã«ã®ãã¼ã¯ãã¦ã³ãã¼ãµãminuteããTypeScriptã使ã£ã¦ãã³ãºãªã³å½¢å¼ã§ä½ã£ã¦ãããã¨ã§ãã¼ã¯ãã¦ã³ã®å é¨å®è£ ã¸ã®ç解ãæ·±ãã¾ãã
â»ãã¼ã¯ãã¦ã³ãã¼ãµã®éçºã¯çè ã®è¶£å³ã§ãããã¸ã«ã«ã®æ¥åã¨ã¯ãªãã®é¢ä¿ãããã¾ããã
- 1. ãã¼ã¯ãã¦ã³ã¨ã¯
- 2. ãã¼ã¯ãã¦ã³ã®æ¹è¨
- 3. ãã¼ã¯ãã¦ã³ãã¼ãµæ¦è¦
- 4. å¦çç³»ã®æ¦è¦
- 5. minute
- 6. ããã±ã¼ã¸ã®ä½æ
- 7. ãã¼ã¯ã³ãä½ã
- 8. åå¥è§£æå¨(Lexer)ã®ã²ãªåãä½ã
- 9. æ§æ解æå¨(Parser)ã®ã²ãªåãä½ã
- 10. æ§æ解æå¨ã®ä½ãè¾¼ã¿
- 11. ã³ã¼ãçæå¨(Generator)ãä½ã
- 12. ãªã¹ãè¦ç´ ã®å®è£
- 12.4 ãªã¹ãã¨å¤ªåã®çµã¿åãã
- 13. çµããã«
- We are hiring!
ãã®è¨äºã¯å
æ¥å¼ç¤¾ã®ç¤¾å
åå¼·ä¼(ããã¯ãã¼ã¯)ã§çºè¡¨ãããã®ãåæ§æãããã®ã¨ãªã£ã¦ãã¾ãã以ä¸ã¯çºè¡¨æã«ä½¿ã£ãè³æã§ãã(ãªãæ¬è¨äºã§æ¸ããã½ããã¦ã§ã¢ã®æ§æã¯çºè¡¨æã¨æ¯ã¹ã¦å¤ãã£ã¦ããã¾ã)
1. ãã¼ã¯ãã¦ã³ã¨ã¯
ãã®è¨äºãèªãã§ããããæ¹ã¯ãåç¥ã®æ¹ãå¤ãã¨æãã¾ããããã¼ã¯ãã¦ã³ã¨ã¯ä½ãã«ã¤ãã¦è§¦ãã¦ããã¾ãã
以ä¸ããã¼ã¯ãã¦ã³ã§æ¸ãããããã¹ãã®ä¾ã§ãã
# Heading 1 * List * nested List **bold**__italic__
ããæ¸ãã¨ä»¥ä¸ã®ãããªHTMLã«å¤æãã¦ããã¾ãã
<h1>Heading 1</h1> <ul> <li>List <ul><li>nested List</li></ul> </li> </ul> <strong>bold</strong><i>italic</i>
å®éã«ãã©ã¦ã¶ã§è¡¨ç¤ºããã¨ãã®ããã«ãªãã¾ãã
ã¤ã¾ããã¼ã¯ãã¦ã³ã¯HTMLã楽ã«çæããããã®è¨æ³ã¨è¨ãã¾ãã
2. ãã¼ã¯ãã¦ã³ã®æ¹è¨
ä¾ã«æãããã¼ã¯ãã¦ã³ã¯ããã¾ã§ããã使ããããã¼ã¯ãã¦ã³ã®è¨æ³ãã§ãããªãããã¼ã¯ãã¦ã³ã®ææ³ã¨ãã¦å®ãããããã®ã§ã¯ããã¾ããããã以åã«ãã¼ã¯ãã¦ã³ã«ã¯å®ã¾ã£ã(ãã¡ãã¨åæã®åããã)è¦æ ¼ãåå¨ããªãã®ã§ãã
ã§ãã®ã§ãã¼ã¯ãã¦ã³ãã¼ãµãã¨ã«å°ããã¤è¨æ³ã«éããããããããä¾ãã°ä»¥ä¸ã«ç¤ºããããªæ¹è¨ã¨ãã¦ç¥ããã¦ãã¾ãã
- GitHub Flavored Markdown
- GitLab Flavored Markdown
- mrkdwn (Slack)
ãããã®ãã¼ã¯ãã¦ã³ã¯ã ãããåãæ¸ãæ¹ã«ãªãããã«è¨è¨ããã¦ãã¾ãããããããç¬èªã«æ¡å¼µããã¦ããè¨æ³ã«è¥å¹²ã®éããããã¾ãã
2.1 CommonMark
ãã®ããµã¼ãã¹ãã¨ã®ãã¼ã¯ãã¦ã³ã®å¾®å¦ãªéããã解æ¶ããããã«çã¾ããã®ãCommonMarkã§ããã§ããçµå±GitHub Flavored Markdownã«ã¯åã¦ãCommonMarkã¯ããã¡ã¯ãã¨ã¯ãªãã¾ããã§ããã
ãªãGitHub Flavored Markdownã«ãã¡ããã¨ããä»æ§ãåå¨ãã¾ãã
ãã®ãããã®æ´å²ã®è©±ã¯ä»¥ä¸ã®2è¨äºãã¨ã¦ãåãããããã£ãã§ãã
- Standard MarkdownãCommon Markdowãããã¦CommonMarkã«
- GitHub Flavored Markdown ã¯ä½ã§ãã£ã¦ä½ã§ãªãã
3. ãã¼ã¯ãã¦ã³ãã¼ãµæ¦è¦
ä¸ã«æ¸ããããã«ããã¼ã¯ãã¦ã³ã®æ¹è¨ã¯ãããããç¬èªã®å®è£ ã§ãããã£ã¦ãã®è¨äºã§ã¯Minute Flavored Markdownã®å¦çç³»ãä½ã£ã¦ãããã¨ã«ãªãã¾ãã
ãã¼ã¯ãã¦ã³ãã¼ãµãå®è£ ãã¦ã¿ãããªã£ãã¨ãããããæ£è¦è¡¨ç¾ã ãã§ååãã¨å¤æãã¦å®è£ ãããã¨ãããã¾ããå®éã«Googleã§æ¤ç´¢ããã¨æ£è¦è¡¨ç¾ã使ã£ããã¼ã¯ãã¦ã³ãã¼ãµã®å®è£ ãçµæ§ãªæ°åºã¦ãã¾ãããããããã¼ã¯ãã¦ã³ã¯æã£ããããè¤éãªã®ã§ãã以ä¸ã«ä¾ã示ãã¾ãã
3.1 è¤æ°è¡ã«æ¸¡ãè¦ç´ ãè¦ç´ ã®ãã¹ã
|col1|col2|col3| |:-|:-:|-:| |**bold**|[link with ~~si~~](https://example.com)|__italic**bold**__| |left|center|right|
ä¸ã®ãã¼ã¯ãã¦ã³ã¯ãã¼ãã«è¦ç´ ã表ãã¦ãã¾ãããã¼ãã«è¦ç´ ã¯2è¡ä»¥ä¸(æä½è¡æ°1ãªã3è¡ä»¥ä¸)ã§è¡¨ç¾ããã1è¡ç®ããããã«è¡¨ç¤ºãããååã2è¡ç®ãalignæå®ã3è¡ç®ä»¥éãå®éã®è¡ã¨ãªã£ã¦ãã¾ããããã¦ãããã®è¡ã¯ä»»æã®é çªã«å ¥ãæ¿ãããã¨ãã§ãã¾ãããã¤ã¾ãè¡ãã¨ã®é çªã«æå³ãåå¨ããè¦ç´ ã¨ãããã¨ã§ãã
1è¡ãã¨ã«å¦çãã¦ããå ´åããã®ãããªè¦ç´ ã¯ã©ããã¦ããç¶æ ããå¿ è¦ã¨ãªã£ã¦ãã¾ãããããåæ°ã¯ä»»æã®æ°å¢ãããã¨ãã§ããã®ã§ããããæ£è¦è¡¨ç¾ã®ã¿ã§æ½åºããã¨ã¡ã³ããã³ã¹æ§ãé常ã«æªããªã£ã¦ãã¾ãããã§ãã
3.2 ãã¡ããã¡ãã®ãªã¹ã
ãã¦ã以ä¸ã®ãããªãã¼ã¯ãã¦ã³ã§è¨ããããªã¹ããè¦ã¦ã¿ã¾ãããã
* é ç®1 * 3æåã¤ã³ãã³ã * 1æåã¤ã³ãã³ã * 4æåã¤ã³ãã³ã
ãã¼ã¯ãã¦ã³ã®ãªã¹ãã¯ããã¤ã³ãã³ãã®æ°ã§ãã¹ãã®æ·±ãã決ããã®ã§ãããä¸ã®ä¾ã¯ã¤ã³ãã³ãã®æåæ°ããã¡ããã¡ãã§ãããã®ãªã¹ããã©ã®ããã«è¡¨ç¤ºããããã¯ããããã®ãã¼ãµã®å®è£ ã«ããã¾ããããä¸è¦ãã¦ã©ã®ãããªHTMLãåºåããããããåããã¾ãããã
ãã®ãã¡ããã¡ãã®ãªã¹ããã©ãã«ãHTMLã«å¤æãã¦æ¬²ããã®ã§ãã(ãã¡ããã¤ã³ãã³ãã®æåæ°ãåºå®ãã¦ãã®ãããªãªã¹ãããã¼ã¹ã§ããªãããã«ããã¨ããã®ãæã§ã¯ãã)
ãã®ãããªå ¥åããããã¨ãèããã¨ãæ£è¦è¡¨ç¾ã ãã§ãã¼ã¯ãã¦ã³ãã¼ãµãå®è£ ããã®ã¯å¿ãã¨ãªããªã£ã¦ãã¾ããã
ãªããã®ãããªã¤ã³ãã³ããæå³ãæã¤å ¥åã¯ãªã¹ãã ãã§ã¯ãªãå¼ç¨ã§ãçºçãã¾ãã
4. å¦çç³»ã®æ¦è¦
ããã§ã¯ããããminuteå¦çç³»ã®è©±ããã¾ããminuteå¦çç³»ã®æ§æè¦ç´ ã¯3ã¤ã§ãåå¥è§£æå¨ãæ§æ解æå¨ãã³ã¼ãçæå¨ã¨ãªã£ã¦ãã¾ããããããã«ã¤ãã¦èª¬æãã¾ãããªã以ä¸ã¯æ¦è¦å³ã§ãã
4.1 åå¥è§£æå¨(Lexer)
åå¥è§£æå¨ã¯å ¥åãããããã¹ãããã¼ã¯ã³ã¨å¼ã°ãããã¼ã¿ã«å¤æããé¨åã§ãã
ä¸è¬çãªå¦çç³»ã§ã¯ã¾ãããã¹ããåå¥è§£æå¨ã§ãã¼ã¯ã³ã«å¤æãã¦ãããæ§æ解æå¨ã«å ¥åãããã¨ãå¤ãã§ãããããminuteã®åå¥è§£æå¨ã¯ãã¼ã¯ã³çæã®ä¾¿å©é¢æ°ãç½®ãã«ã¨ã©ãã¦ãã¾ããããã¯ãã¼ã¯ãã¦ã³ã«ã¯ä½åãªã¹ãã¼ã¹ãªã©ããã°ã©ãã³ã°è¨èªã«ããããä½åãªæåãåå¨ããªãããã§ãã
4.2 æ§æ解æå¨(Parser)
æ§æ解æå¨ã¯ãã¼ã¯ã³åãå ¥åã¨ãã¦æ½è±¡æ§ææ¨(AST)ãæ§ç¯ãã¾ãã
ããããã¼ã¯ãã¦ã³ã®ææ³ã解éããé¨åã§ããä¸è¬çãªã³ã³ãã¤ã©ãã¤ã³ã¿ããªã¿ã§ã¯ããã§æ§æã¨ã©ã¼ãçºè¦ããã®ã§ããããã¼ã¯ãã¦ã³ã¯ééã£ãæ§æã¯ãã®ã¾ã¾ã®ããã¹ãã¨ãã¦åºåãã¾ãã
4.3 ã³ã¼ãçæå¨(Generator)
ã³ã¼ãçæå¨ã¯æ½è±¡æ§ææ¨ãå ¥åã¨ãã¦ã¿ã¼ã²ããã¨ãªãè¨èªã®ã³ã¼ããçæãã¾ããããã§ã¯HTMLãåºåãã¾ãã
5. minute
ã§ã¯ä»ããå®éã«ãã³ãºãªã³å½¢å¼ã§minuteãã¼ã¯ãã¦ã³ãã¼ãµãä½ã£ã¦ããã¾ãã
ãã®è¨äºã§ã¯ããã¼ã¯ãã¦ã³ã®ãã¹ã¦ã®è¨æ³ãã«ãã¼ããã¨è¨äºãã¨ã¦ãé·ããªã£ã¦ãã¾ãä¸ã«åé·ãªè¨è¿°ãå¤ããªãããã太å(<strong>
ã¿ã°)ã¨ãªã¹ã(<ul><li>
ã¿ã°)ã«éå®ãã¦å®è£
ãã¦ããã¾ãã
ç®æ¨ã¯ä»¥ä¸ã®ãã¼ã¯ãã¦ã³ã
* **bold** * list
以ä¸ã®ãããªHTMLã«å¤æããããã¨ã§ãã
<ul> <li><strong>bold</strong></li> <li>list</li> </ul>
6. ããã±ã¼ã¸ã®ä½æ
ã§ã¯å®éã«ããã±ã¼ã¸ãä½æãã¦ããã¾ãã
mkdir minute cd minute yarn init yarn add --dev typescript ts-node ./node_modules/.bin/tsc --init mkdir src
ts-node
ã§TypeScriptã®ãã¡ã¤ã«ãç°¡åã«å®è¡ã§ããããã«package.jsonã®scriptsã«ä»¥ä¸ã追å ãã¾ãã
"scripts": { "exec": "ts-node src/index.ts" },
ããã¦æ¬¡ã«src/index.ts
ãä½æãã¾ãããã®é¢æ°ããã¼ã¯ãã¦ã³ãHTMLã«å¤æããæ¬ä½ã¨ãªã£ã¦ãã¾ãã
// src/index.ts const convertToHTMLString = (markdown: string) => { return markdown; }
æå§ãã«index.ts
ã®æ«å°¾ã«ä»¥ä¸ã追å ãã¦yarn run exec
ãã¨ã©ã¼ãªãå®è¡ãããã°æºåã¯å®äºã§ãã
// src/index.ts (ç¥) console.log(convertToHTMLString('Hello World!'))
7. ãã¼ã¯ã³ãä½ã
ãã¼ã¯ã³ã®åãä½ã£ã¦ããã¾ãããã¼ã¯ã³ãªã©ã®åãå®ç¾©ãããã£ã¬ã¯ããªã¨ãã¦src/models
ãä½æãã¾ãã
mkdir src/models
src/models/token.ts
ã«ä»¥ä¸ãå®ç¾©ãã¦ãã ããã
// src/models/token.ts export type Token { id: number; parent: Token; elmType: string; content: string; }
ããããã®è¦ç´ ã®èª¬æã¯ä»¥ä¸ã§ãã
å±æ§å | 説æ |
---|---|
id | ãã®è¡ã表ããã¼ã¯ã³åã®ä¸ã§ã¦ãã¼ã¯ã¨ãªãidã§ãããã¼ã¸æ¸ã¿ãã¼ã¯ã³ã®ä½ç½®ã調ã¹ãããå¿ è¦ã«ãªãã¾ãã(å¾è¿°) |
parent | 親ãã¼ã¯ã³ã§ã |
elmType | è¦ç´ ã®ç¨®å¥('bold'ã'italic'ãªã©)ã§ã |
content | ãã¼ã¯ã³ã®ä¸èº«ã§ã |
8. åå¥è§£æå¨(Lexer)ã®ã²ãªåãä½ã
次ã«åå¥è§£æå¨ãä½ãã¾ããsrc/lexer.ts
ãä½æãã¾ããminuteã§ã®åå¥è§£æå¨ã®å½¹å²ã¯ãã¾ãå¤ããªããæ£è¦è¡¨ç¾ã¨ããã¤ãã®ä¾¿å©é¢æ°ãç½®ãã«ã¨ã©ã¾ãã¾ãã
8.1 æ£è¦è¡¨ç¾ã¨ä¾¿å©é¢æ°ãã¡
src/lexer.ts
ã以ä¸ã®ããã«å¤æ´ãã¾ããSTRONG_ELM_REGXP
ã¯å¤ªå(****
)ã«ãããããæ£è¦è¡¨ç¾ã¨ãªã£ã¦ãã¾ãã
// src/lexer.ts import { Token } from './models/token' const TEXT = 'text'; const STRONG = 'strong'; const STRONG_ELM_REGXP = /\*\*(.*?)\*\*/ const genTextElement = (id: number, text: string, parent: Token): Token => { return { id, elmType: TEXT, content: text, parent, }; } const genStrongElement = (id: number, text: string, parent: Token): Token => { return { id, elmType: STRONG, content: '', parent, }; } const matchWithStrongRegxp = (text: string) => { return text.match(STRONG_ELM_REGXP); } export { genTextElement, genStrongElement, matchWithStrongRegxp }
ããã§åå¥è§£æå¨ã®ã²ãªåãã§ãã¾ããã
9. æ§æ解æå¨(Parser)ã®ã²ãªåãä½ã
次ã«æ§æ解æå¨(Parser)ãä½ãã¾ããsrc/parser.ts
ãä½æãã¾ãããã®ãã¡ã¤ã«ã«ã¯parse()
é¢æ°ãç½®ãã¾ãã
åå¥è§£æã¨æ§æ解æã¯ãã¼ã¯ãã¦ã³ããã¹ã1è¡ãã¨ã«åããã¦è¡ãã¾ãã
ãã®é¢æ°ã¯ãã¼ã¯ãã¦ã³ã1è¡ãã¨ã«æ§æ解æå¨(Parser)ã«ããã¦ãåè¡ã®ãã¼ã¯ã³ãæ¨æ§é ã«ä¸¦ã¹ãé åãè¿ãã¾ãããã®ãã¼ã¯ã³ã®é åãæ½è±¡æ§ææ¨(AST)ã«ãªãã¾ãã
9.1 parse()
é¢æ°
æ§æ解æå¨ã®æ¬ä½ã¨ãªãé¢æ°ãå®è£ ãã¾ãããã®é¢æ°ã¯1è¡ã®ãã¼ã¯ãã¦ã³ãå ¥åã¨ãã¦ããã®è¡ã®æ½è±¡æ§ææ¨ãåºåãã¾ãã
// src/parser.ts export const parse = (markdownRow: string) => { return markdownRow }
ããã¦ãã®é¢æ°ã使ãããã«src/index.ts
ãå¤æ´ãã¾ãã
// src/index.ts import { parse } from './parser' const convertToHTMLString = (markdown: string) => { const mdArray = markdown.split(/\r\n|\r|\n/); const asts = mdArray.map(md => parse(md)); return asts }
æ½è±¡æ§ææ¨ãè¤æ°ããã®ã¯ã1è¡ãã¨ã«1ã¤ã®æ½è±¡æ§ææ¨ãçæãããããã§ãã
9.2 æ½è±¡æ§ææ¨
ãããè¦ã¦ããæ¹ã¯æ½è±¡æ§ææ¨ã¨ããè¨èã¯è³ã«ãããã¨ãããããããã¾ãããæ½è±¡æ§ææ¨ã¨ã¯ãå
¥åããè¨èªã®æå³ã«é¢ä¿ããªãé¨åãåãé¤ãããã¼ãã§æ§æããæåæ¨ãã®ãã¨ã§ããä¾ãã°ãã¼ãã«è¦ç´ ãæ¸ãã¨ãã«åºã¦ãã|
ã¯ãã¼ã¯ã³åãããã¨ãã«æ¶ãããããªã©ã§ãã
ãã®æ½è±¡æ§ææ¨ãã³ã¼ãçæå¨ã«å ¥åãã¦æçµçãªçæç©ã§ããHTMLãçæãã¾ããä¾ãã°ä»¥ä¸ã®ãããªæåæ¨ãæ½è±¡æ§ææ¨ã§ãã
10. æ§æ解æå¨ã®ä½ãè¾¼ã¿
æ§æ解æå¨ãä½ãè¾¼ãã§ããåã«ã¡ãã£ã¨ããæºåã¨ãã¦æ§ææ¨ã®ã«ã¼ãã表ãrootToken
ã追å ãã¾ãã
// src/parser.ts import { Token } from './models/token' const rootToken: Token = { id: 0, elmType: 'root', content: '', parent: {} as Token, }; export const parse = (markdownRow: string) => { return markdownRow }
10.1 æ½è±¡æ§ææ¨ã®çæ(1) (****
ã空ã®å¤ªå)
ã¾ãã¯ç©ºã®å¤ªå(****
)ã«ãããããããã¹ãããã¼ã¯ã³åããã³ã¼ããæ¸ãã¦ããã¾ãããããªãã³ã¼ãéãå¢ãã¾ãããsrc/parser.ts
ã以ä¸ã®ããã«å¤æ´ãã¾ãã
// src/parser.ts (åç¥) export const parse = (markdownRow: string) => { return _tokenizeText(markdownRow) // _tokenizeText()ã使ãããã«å¤æ´ } const _tokenizeText = ( textElement: string, initialId: number = 0, initialRoot: Token = rootToken ) => { let elements: Token[] = []; let parent: Token = initialRoot; let id = initialId; const _tokenize = (originalText: string, p: Token) => { let processingText = originalText; parent = p; // ãã®è¡ã空æåã«ãªãã¾ã§å¦çãç¹°ãè¿ã while (processingText.length !== 0) { const matchArray = matchWithStrongRegxp(processingText) as RegExpMatchArray; id += 1; const elm = genStrongElement(id, '', parent) // Set the outer element to parent parent = elm; elements.push(elm); processingText = processingText.replace(matchArray[0], ''); // å¦çä¸ã®ããã¹ããããã¼ã¯ã³ã«ããããã¹ããåé¤ãã _tokenize(matchArray[1], parent); // å帰ã§æã parent = p; } }; _tokenize(textElement, parent); return elements; };
_tokenizeText()
é¢æ°ã®ä¸ã«ããã«_tokenize()
é¢æ°ãå®ç¾©ãã¦ãã¾ãããã®é¢æ°ããã¼ã¯ã³åã®æ¬ä½ã§ãããå帰ã使ã£ã¦è¦ç´ ãæã£ã¦ãã¾ãããã®ããã«å帰ãç¨ããæ§æ解æã®ãã¨ãå帰çä¸åãæ§æ解æ(recursive descent parsing)ã¨ããã¾ãã
ã¾ããé
åelements
ãæ½è±¡æ§ææ¨ã表ãå¤æ°ã¨ãªã£ã¦ãã¾ããããã§ãã®é
åã«è¦ç´ ãpushãã¦ãããã³ã¼ãçæå¨ã§ä½¿ãã¨ãã¯popã§å¦çãã¦ãããã¨ã§ã¹ã¿ãã¯ã®ããã«æ±ãã¾ãã
ããã¦src/index.ts
ã®æ«å°¾ã«ä»¥ä¸ã追å ãã¦yarn run exec
ãå®è¡ããã¨ä»¥ä¸ã®ãããªåºåãå¾ããã¾ãã
(åç¥) console.log(convertToHTMLString('****')
ã¾ãããã®åºåã®æ½è±¡æ§ææ¨ã¯ä»¥ä¸ã®ãããªãã®ã«ãªãã¾ãã
10.2 æ½è±¡æ§ææ¨ã®çæ(2) (**bold**
)
ãã¦ã空ã®å¤ªåã¯ãã¼ã¹ã§ãã¾ãããã§ã¯æ¬¡ã«ä¸èº«ãå
¥ã£ã**bold**
ããã¼ã¹ããå¦çãæ¸ãã¾ãã
**bold**
ãæ§æãããã¼ã¯ã³ã¯**
ã®ã¿ã°ãã¼ã¯ã³ã¨bold
ã®ããã¹ããã¼ã¯ã³ã®2種é¡ã§ããã§ãã®ã§ãã®2種é¡ã«å¯¾å¿ããå¿
è¦ãããã¾ãã
// src/parser.ts (åç¥) const _tokenizeText = ( textElement: string, initialId: number = 0, initialRoot: Token = rootToken ) => { let elements: Token[] = []; let parent: Token = initialRoot; let id = initialId; const _tokenize = (originalText: string, p: Token) => { let processingText = originalText; parent = p; // ãã®è¡ã空æåã«ãªãã¾ã§å¦çãç¹°ãè¿ã while (processingText.length !== 0) { const matchArray = matchWithStrongRegxp(processingText); // ****ã«ãããããªãã£ãè¦ç´ ãã¤ã¾ãããã§ããã¹ããã¼ã¯ã³ãä½ã if (!matchArray) { id += 1; const onlyText = genTextElement(id, processingText, parent); processingText = ''; elements.push(onlyText); } else { id += 1; const elm = genStrongElement(id, '', parent) // Set the outer element to parent parent = elm; elements.push(elm); processingText = processingText.replace(matchArray[0], ''); // å¦çä¸ã®ããã¹ããããã¼ã¯ã³ã«ããããã¹ããåé¤ãã _tokenize(matchArray[1], parent); // å帰ã§æã parent = p; } } }; _tokenize(textElement, parent); return elements; };
src/index.ts
ã®æ«å°¾ã«ä»¥ä¸ã追å ãã¦yarn run exec
ãå®è¡ããã¨ä»¥ä¸ã®ãããªåºåãå¾ããã¾ãã
// src/index.ts console.log(convertToHTMLString('**bold**')
ã¾ãããã®åºåã®æ½è±¡æ§ææ¨ã¯ä»¥ä¸ã®ãããªãã®ã«ãªãã¾ãã
10.3 æ½è±¡æ§ææ¨ã®çæ(3) (normal**bold**
)
次ã«å¤ªåãã¼ã¯ã³ã®å·¦ã«Textãã¼ã¯ã³ãåå¨ãããã¼ã¯ãã¦ã³ã®æ½è±¡æ§ææ¨ãçæã§ããããã«ãã¾ãã以ä¸ã®ifç¯ãgenStrongElement()
ãå®è¡ãã¦ããé¨åã®ä¸ã«é
ç½®ãã¾ãã
if (Number(matchArray.index) > 0) { // "aaa**bb**cc" -> TEXT Token + "**bb**cc" ã«ãã const text = processingText.substring(0, Number(matchArray.index)); id += 1; const textElm = genTextElement(id, text, parent); elements.push(textElm); processingText = processingText.replace(text, ''); // å¦çä¸ã®ããã¹ããããã¼ã¯ã³ã«ããããã¹ããåé¤ãã }
ãã®ifæã¯normal**bold**
ã®ãããªå ´åã«ããããããã®ã§ããã®ã¨ãmatchArray.indexã«ã¯6ãå
¥ã£ã¦ãã¾ããããã¦genTextElement()
ã§normal
ã®Textãã¼ã¯ã³ãä½ããprocessingText
ããnormal
ãé¤å»ãã¦**bold**
ã«ãã¾ãã
以ä¸é¢æ°å ¨æã§ãã
// src/parser.ts (åç¥) const _tokenizeText = ( textElement: string, initialId: number = 0, initialRoot: Token = rootToken ) => { let elements: Token[] = []; let parent: Token = initialRoot; let id = initialId; const _tokenize = (originalText: string, p: Token) => { let processingText = originalText; parent = p; // ãã®è¡ã空æåã«ãªãã¾ã§å¦çãç¹°ãè¿ã while (processingText.length !== 0) { const matchArray = matchWithStrongRegxp(processingText); // ****ã«ãããããªãã£ãè¦ç´ ãã¤ã¾ãããã§ããã¹ããã¼ã¯ã³ãä½ã if (!matchArray) { id += 1; const onlyText = genTextElement(id, processingText, parent); processingText = ''; elements.push(onlyText); } else { // ããã®ifæã追å é¨å if (Number(matchArray.index) > 0) { // "aaa**bb**cc" -> TEXT Token + "**bb**cc" ã«ãã const text = processingText.substring(0, Number(matchArray.index)); id += 1; const textElm = genTextElement(id, text, parent); elements.push(textElm); processingText = processingText.replace(text, ''); // å¦çä¸ã®ããã¹ããããã¼ã¯ã³ã«ããããã¹ããåé¤ãã } id += 1; const elm = genStrongElement(id, '', parent) // Set the outer element to parent parent = elm; elements.push(elm); processingText = processingText.replace(matchArray[0], ''); // å¦çä¸ã®ããã¹ããããã¼ã¯ã³ã«ããããã¹ããåé¤ãã _tokenize(matchArray[1], parent); // å帰ã§æã parent = p; } } }; _tokenize(textElement, parent); return elements; };
ããã¦src/index.ts
ã®æ«å°¾ã«ä»¥ä¸ã追å ãã¦yarn run exec
ãå®è¡ããã¨ä»¥ä¸ã®ãããªåºåãå¾ããã¾ãã
// src/index.ts console.log(convertToHTMLString('normal**bold**')
ãã®æ½è±¡æ§ææ¨ã¯ä»¥ä¸ã®ãããªå³ã«ãªãã¾ãã
11. ã³ã¼ãçæå¨(Generator)ãä½ã
ã§ã¯æå¾ã®å·¥ç¨ã¨ãã¦ã³ã¼ãçæå¨ãä½ã£ã¦ããã¾ããã³ã¼ãçæå¨ã¯æ½è±¡æ§ææ¨ãå ¥åã¨ãã¦HTMLæååãåºåãããã®ã§ãã
ããããåµæ工夫ãåºã¦ããé¨åã§ãã®ã§å°ã詳ãã説æãã¾ãã
11.1 å¦çã®æµã
ãã£ããã¨ä»¥ä¸ã®ãããªæµãã«ãªã£ã¦ãã¾ãã
- æ½è±¡æ§ææ¨ãããã¼ã¯ã³ãFILOé ã«åãåºã
- ãã¼ã¯ã³ã®contentãè¦ã¦HTMLã®ã¿ã°ãçæãã
- 親ãã¼ã¯ã³ã¨ãã¼ã¸ãã¦è¦ªãã¼ã¯ã³ããã¼ã¸æ¸ã¿ãã¼ã¯ã³ã«ãã
- ãã¹ã¦ã®ãã¼ã¯ã³ã®è¦ªãRootã«ãªãã¾ã§ç¹°ãè¿ã
- Root以ä¸ã®ãã¼ã¯ã³åã®contentãçµåãã
æ¬è¨äºã§ã¯ã¤ã¿ãªãã¯ãå®è£
ãã¾ãããããã¹ãããæã®æ§åãåããããã«normal**left__italic__right**
ã¨ãããã¼ã¯ãã¦ã³ããå¾ãããæ½è±¡æ§ææ¨ãä¾ã¨ãã¦ã³ã¼ãçæå¨ã®å¦çã追ã£ã¦ããã¾ãã
ãã¡ãããã®æ½è±¡æ§ææ¨ã¨ãªãã¾ãã
ããã§ã¯æ½è±¡æ§ææ¨ã表ããã¼ã¯ã³ã®é åãå¾ãããå¦çãã¦ããã¾ããå¾ãããå¦çãããã¨ã§FILOã¹ã¿ãã¯ã®åãåºãé ã§ãã¼ã¯ã³ãåãåºããã¨ãã§ãã¾ãã
ãªããã¼ã¯ã³ã®é åã¯ä¸ã®å³ã®çªå·é ã«ä¸¦ãã§ãã¾ã(æ½è±¡æ§ææ¨ã®é åã¯å帰ã使ã£ã¦æ§ç¯ããã¦ããããæ·±ãåªå æ¢ç´¢ã®é çªã«ä¸¦ãã§ãã¾ã)ã
ããã§ãã¼ã¸æ¸ã¿ãã¼ã¯ã³(MergedToken
)ã¨ãããã®ãåºã¦ãã¾ããããã¯è¤æ°ã®ãã¼ã¯ã³ã®content
ããã¼ã¸ãããã¼ã¯ã³ã§ãããelmType: 'merged'
ã®ãã®ã§ããå¤åãã¼ã¯ã³ããã¼ã¸ãã¦ASTãä½ãæ¿ãã¦ããä»çµã¿ã¯æ°è¦æ§ãããã¾ãã
ããã¦æ½è±¡æ§ææ¨ãæ§æãããã¼ã¯ã³ã親ãã¼ã¯ã³ã¨ãã¼ã¸ãã¦ãã¼ã¸æ¸ã¿ãã¼ã¯ã³ã«ãã¦ããã¾ããã¤ã¾ãæ§ææ¨ãã®ãã®ãçµã¿æ¿ãã¦ããã¾ãããã®ãã¼ã¸æ¸ã¿ãã¼ã¯ã³ãä½æããéã«(å)ãã¼ã¯ã³ã®content
ããHTMLã¿ã°ãçæãã¾ãã
ãã®å¦çã¯ãã¹ã¦ã®ãã¼ã¯ã³ã®è¦ªãRootã«ãªãã¾ã§(ã¤ã¾ãæ¨ã®æ·±ãã2ã«ãªãã¾ã§)è¡ãã¾ããå®éã«ã¯ä»¥ä¸ã®ãããªããã¼ã§æ½è±¡æ§ææ¨ãæ¸ãæãã¦ããã¾ãã
11.2 ãã¼ã¸æ¸ã¿ãã¼ã¯ã³(MergedToken)
ä¸è¿°ã®ããã«ãã¼ã¸æ¸ã¿ãã¼ã¯ã³ã¯åãã¼ã¯ã³ã®content
ããã¼ã¸ããã親ãã¼ã¯ã³ã¨ãªãã¾ãã
ã§ã¯src/models/merged_token.ts
ã以ä¸ã®ããã«ä½æãã¾ãããã¼ã¿æ§é ã¯ã»ã¨ãã©Token
ã¨å¤ãããªãã§ãããparent
ãMergedToken
ãåãããã¨ããç¹ãå¤ãã£ã¦ãã¾ãã
// src/models/merged_token.ts import { Token } from './token' export type MergedToken = { id: number; elmType: 'merged'; content: string; parent: Token | MergedToken; };
11.3 generator()
ã®ä½æ
src/generator.ts
ãä½æãã¾ããã¾ãã¯Textãã¼ã¯ã³ããã®ã¾ã¾è¡¨ç¤ºãããããªãã®ãå®è£
ãã¾ããã¾ã 太åã¯ç¡è¦ããã¾ãã
generator()
ã¯å¼æ°ã¨ãã¦Token[][]
ãåãã¾ããããã¯1è¡ãã¨ã«1ã¤ã®æ½è±¡æ§ææ¨(Token[]
)ãåå¨ããToken[][]
ã¯ãã®ãã¡ã¤ã«å
¨è¡åã表ãããã§ãã
ãã®é¢æ°ã§ã¯1è¡ãã¤æ½è±¡æ§ææ¨ãå¦çãã¦çµæã®HTMLæååãçµåãã¦ããã¾ãã
// src/generator.ts import { Token } from './models/token' import { MergedToken } from './models/merged_token' const _generateHtmlString = (tokens: Array<Token | MergedToken>) => { return tokens .map((t) => t.content) .reverse() .join(''); }; const generate = (asts: Token[][]) => { const htmlStrings = asts.map((lineTokens) => { let rearrangedAst: Array<Token | MergedToken> = lineTokens.reverse(); return _generateHtmlString(rearrangedAst); }); return htmlStrings.join(''); }; export { generate };
ããã¦generate()
é¢æ°ã使ãããsrc/index.ts
ãå¤æ´ãã¾ãã
// src/index.ts import { parse } from './parser' import { generate } from './generator' const convertToHTMLString = (markdown: string) => { const mdArray = markdown.split(/\r\n|\r|\n/); const asts = mdArray.map(md => parse(md)); const htmlString = generate(asts) return htmlString }
src/index.ts
ã®æ«å°¾ã«console.log(convertToHTMLString('normal')
ã追å ãã¦yarn run exec
ãã¦ä»¥ä¸ã®ãããªåºåãå¾ãããã°æåã§ãã
11.4 ãã¼ã¯ã³ããã¼ã¸ãããã¦ASTãåæ§ç¯ãã
ããã§ãã¼ã¸æ¸ã¿ãã¼ã¯ã³ã®ä½æã¨æ½è±¡æ§ææ¨ã®ä½ãå¤ããå®è£ ãã¾ãã
_createMergedContent()
ã¯ãã¼ã¸æ¸ã¿ãã¼ã¯ã³ãä½æããã¨ãã«ãã¼ã¯ã³ã®contentããHTMLã¿ã°ãçæããããã¯è¦ªãã¼ã¯ã³ã¨contentããã¼ã¸ããçµæãè¿ãã¾ãã
æ½è±¡æ§ææ¨ãæ§æãããã¹ã¦ã®ãã¼ã¯ã³ã®è¦ªãRootã«ãªã(æ½è±¡æ§ææ¨ã®æ·±ãã2ã«ãªã)ã¾ã§ç¹°ãè¿ãã¾ãã
// src/generator.ts const isAllElmParentRoot = (tokens: Array<Token | MergedToken>) => { return tokens.map((t) => t.parent?.elmType).every((val) => val === 'root'); }; const _getInsertPosition = (content: string) => { let state = 0; const closeTagParentheses = ['<', '>']; let position = 0; content.split('').some((c, i) => { if (state === 1 && c === closeTagParentheses[state]) { position = i; return true; } else if (state === 0 && c === closeTagParentheses[state]) { state++; } }); return position + 1; }; const _createMergedContent = ( currentToken: Token | MergedToken, parentToken: Token | MergedToken ) => { let content = ''; switch (parentToken.elmType) { case 'strong': content = `<strong>${currentToken.content}</strong>`; break; case 'merged': const position = _getInsertPosition(parentToken.content); content = `${parentToken.content.slice(0, position)}${ currentToken.content }${parentToken.content.slice(position)}`; } return content; }; const generate = (asts: Token[][]) => { const htmlStrings = asts.map((lineTokens) => { let rearrangedAst: Array<Token | MergedToken> = lineTokens.reverse(); // ãã¹ã¦ã®ãã¼ã¯ã³ãRootã®ä¸ã«ä»ãã¾ã§ãã¼ã¸ãç¹°ãè¿ã while (!isAllElmParentRoot(rearrangedAst)) { let index = 0; while (index < rearrangedAst.length) { if (rearrangedAst[index].parent?.elmType === 'root') { // Rootã«ãããã¼ã¯ã³ã®å ´åä½ãããªãã index++; } else { const currentToken = rearrangedAst[index]; rearrangedAst = rearrangedAst.filter((_, t) => t !== index); // Remove current token const parentIndex = rearrangedAst.findIndex((t) => t.id === currentToken.parent.id); const parentToken = rearrangedAst[parentIndex]; const mergedToken: MergedToken = { id: parentToken.id, elmType: 'merged', content: _createMergedContent(currentToken, parentToken), parent: parentToken.parent, }; rearrangedAst.splice(parentIndex, 1, mergedToken); // parentã¨ãã¼ã¸ããã // ã¤ã¾ã2ã¤å¤æ´ãããåã¯åé¤ã親ã¯ç½®ãæãã // 1ã¤è¦ªã¨åæããã1ã¤è¦ç´ ãæ¶ããã®ã§indexã¯å¤ãããããªã®ã§ãã¼ã¸ããªãæã®ã¿index++ããã } } } return _generateHtmlString(rearrangedAst); }); return htmlStrings.join(''); };
ããã§ããããid
ãåºã¦ãã¾ããid
ã¯æ½è±¡æ§ææ¨ãã親è¦ç´ ã®ã¤ã³ããã¯ã¹ãæ¢ãããã«ä½¿ããã¾ãã
12. ãªã¹ãè¦ç´ ã®å®è£
ããã¾ã§ã§æ¬è¨äºã®ç®æ¨ã¨ãã太åããªã¹ãã®å®è£ ã®ãã¡å¤ªåé¨åãçµããã¾ãããã§ã¯æ¬¡ã«ãªã¹ããå®è£ ãã¦ããã¾ãã以ä¸ã®ãããªãã¼ã¯ãã¦ã³ã
* list1 * list2
ãã®ãããªHTMLã«å¤æãã¾ãã
<ul> <li>list1</li> <li>list2</li> </ul>
ãªããªã¹ãã®ãã¹ããå®è£ ããã¨è¤éã«ãªãããããããããã§ã¯å®è£ ããªããã¨ã¨ãã¾ã(ç§ãä½ã£ãå®æå½¢ã«ã¯å®è£ ããã¦ã¾ã)ããªã¹ãã®ãã¹ãã«ä½¿ãããã¤ã³ãã³ãã®æåæ°ã¯ä»»æã ãã¤ã³ãã³ããæ¸ã£ããå¢ããããããã§ããããããé¨åã§ãã
12.1 å¦çã®æµã
ã¾ãããã®ãªã¹ãè¦ç´ ãªã®ã§ãããå ç¨å®è£ ãã太åã¨ã¯å¤§ããéãé¨åãããã¾ããããã¯è¤æ°è¡ã«ã¾ããã£ã¦ããã¨ãããã¨ã§ãã
以ä¸ã®ãããªå¤ªåã¨ãªã¹ãã®æ··ãã£ããã¼ã¯ãã¦ã³ãããã¨ãããªã¹ããã¿ã¤ããã<ul>
ã¿ã°ãéå§ãå
é¨ã«<li>
ã¿ã°ãé
ç½®ãããã®å¾ç©ºè¡ãè¦ã¤ããã¿ã¤ãã³ã°ã§</ul>
ã¿ã°ã§éããªããã°ããã¾ããã
normal**bold** * list1 * list2 **bold2**
ãããå®ç¾ããããã«ã¯ãã¼ã¯ãã¦ã³ã®è§£éä¸ã«ç¾å¨ã®ç¶æ ãæã¤å¿ è¦ãããã¾ãã
ãã®ãã¼ã¯ãã¦ã³ã«ããã¦2è¡ç®ã¾ã§ã¯NEUTRAL
ç¶æ
ã ã£ãã®ã3è¡ç®ãã4è¡ç®ã§ã¯LIST
ç¶æ
ã«å
¥ã5è¡ç®ã§NEUTRAL
ã«æ»ããã¨ãã£ãå
·åã§ãã
ãã®ç¶æ é·ç§»å³ãé決å®æ§æéãªã¼ãããã³(NFA)ã¨ãã¦æ¸ãã¨ä»¥ä¸ã®ããã«ãªãã¾ããããã¯ã12.3 ãªã¹ãè¦ç´ ã®å®è£ (è¤æ°è¡ã®ãªã¹ã)ãã§å®è£ ãã¾ãã
12.2 ãªã¹ãè¦ç´ ã®å®è£ (1è¡ã®ãªã¹ã)
ã¾ãæåã«ä»¥ä¸ã®ãªã¹ãè¦ç´ ãå®è£ ãã¦ããã¾ãã
* list1
ãã®ãã¼ã¯ãã¦ã³ã¯ä»¥ä¸ã®ããã«è§£éãããããã«ãã¾ãã
<ul><li>list1</li></ul>
ã¾ãã¯ä»¥ä¸ã®ããã«lexer.tsã«è¿½è¨ãã¾ãã
// src/lexer.ts (ç¥) const LIST_REGEXP = /^( *)([-|\*|\+] (.+))$/m; const matchWithListRegxp = (text: string) => { return text.match(LIST_REGEXP); } export { genTextElement, genStrongElement, matchWithStrongRegxp, matchWithListRegxp }
ããã¦parser.tsã«tokenizeList()
é¢æ°ã追å ãã¾ãã
// src/parser.ts (ç¥) export const _tokenizeList = (listString: string) => { const UL = 'ul'; const LIST = 'li'; let id = 1; const rootUlToken: Token = { id, elmType: UL, content: '', parent: rootToken, }; let parent = rootUlToken; let tokens: Token[] = [rootUlToken]; const match = matchWithListRegxp(listString) as RegExpMatchArray id += 1; const listToken: Token = { id, elmType: LIST, content: '', // Indent level parent, }; tokens.push(listToken); const listText: Token[] = _tokenizeText4(match[3], id, listToken); id += listText.length; tokens.push(...listText); return tokens; };
ããã¦parse()
é¢æ°ã§ããã使ãããå¤æ´ãã¾ã
// src/parser.ts export const parse = (markdownRow: string) => { if (matchWithListRegxp(markdownRow)) { return _tokenizeList(markdownRow) } return _tokenizeText(markdownRow) }
次ã«ã³ã¼ãçæå¨ãä½ãã¾ãã_createMergedContent()
é¢æ°ã以ä¸ã®ããã«å¤æ´ãã¦ãã ããã
// src/generator.ts const _createMergedContent = ( currentToken: Token | MergedToken, parentToken: Token | MergedToken ) => { let content = ''; switch (parentToken.elmType) { case 'li': content = `<li>${currentToken.content}</li>`; break; case 'ul': content = `<ul>${currentToken.content}</ul>`; break; case 'strong': content = `<strong>${currentToken.content}</strong>`; break; case 'merged': const position = _getInsertPosition(parentToken.content); content = `${parentToken.content.slice(0, position)}${ currentToken.content }${parentToken.content.slice(position)}`; } return content; };
ããã§index.tsã«console.log(convertToHTMLString('* list1'))
ã追è¨ãã¦ä»¥ä¸ã®ãããªåºåãå¾ãããã°æåã§ãã
ãªãããã§è¤æ°è¡ãªã¹ããå
¥å(console.log(convertToHTMLString('* list1\n* list2'))
)ããã¨ä»¥ä¸ã®ããã«ulã¿ã°ãè¤æ°ä½ããã¦ãã¾ãã¾ãã
12.3 ãªã¹ãè¦ç´ ã®å®è£ (è¤æ°è¡ã®ãªã¹ã)
次ã«ãªã¹ãè¦ç´ ã2è¡ã«ãã¾ãã
* list1 * list2
ãã®ãã¼ã¯ãã¦ã³ã¯ä»¥ä¸ã®ããã«è§£éãããããã«ãã¾ãã
<ul><li>list1</li><li>list2</li></ul>
ãªã¹ãè¦ç´ å ¨ä½ã§1ã¤ã®ulã¿ã°ãæã¡ããã®ä¸ã«åè¡ãliã¿ã°ã¨ãã¦å ¥ãè¾¼ãå½¢ã«ãªãã¾ãããªãããã¹ããããããã«ã¯ãã®ä¸ã§æ´ã«ulã¿ã°ã使ããã¨ã«ãªãã¾ãã
æ¹è¡ãå«ãã ãªã¹ãå ¨ä½ã®æåå
ã_tokenizeList()
é¢æ°ã«ã¯æ¹è¡æåãå«ãã ãªã¹ãã®å
¨ä½ãå
¥åããããã«ããããå®è£
ã®ã¢ã¤ãã¢ã¨ãã¾ãã
_tokenizeList()
é¢æ°ã以ä¸ã®ããã«å¤æ´ãã¾ãã
// src/parser.ts export const _tokenizeList = (listString: string) => { const UL = 'ul'; const LIST = 'li'; let id = 1; const rootUlToken: Token = { id, elmType: UL, content: '', parent: rootToken, }; let parent = rootUlToken; let tokens: Token[] = [rootUlToken]; listString.split(/\r\n|\r|\n/).filter(Boolean).forEach((l) => { const match = matchWithListRegxp(l) as RegExpMatchArray id += 1; const listToken: Token = { id, elmType: LIST, content: '', // Indent level parent, }; tokens.push(listToken); const listText: Token[] = _tokenizeText4(match[3], id, listToken); id += listText.length; tokens.push(...listText); }); return tokens; };
ããããåå¥è§£æå¨ãç·¨éãã¦ããã®ã§ãããããã§ããããåå¥è§£æå¨ã«ç¶æ 管çã¨ããæ°ããå½¹å²ã追å ããã¾ãã
ã§ã¯å®éã«ä¸ã§è²¼ã£ããªã¹ãã«é¢ããã¹ãã¼ããã·ã³ãsrc/lexer.tsã«analize()
é¢æ°ã«å®è£
ãã¦ããã¾ãã
// src/lexer.ts const analize = (markdown: string) => { const NEUTRAL_STATE = 'neutral_state'; const LIST_STATE = 'list_state'; let state = NEUTRAL_STATE; let lists = ''; const rawMdArray = markdown.split(/\r\n|\r|\n/); let mdArray: Array<string> = []; rawMdArray.forEach((md, index) => { const listMatch = md.match(LIST_REGEXP); if (state === NEUTRAL_STATE && listMatch) { state = LIST_STATE; lists += `${md}\n`; } else if (state === LIST_STATE && listMatch) { // æå¾ã®è¡ããªã¹ãã ã£ãå ´å if (index === rawMdArray.length - 1) { lists += `${md}`; mdArray.push(lists) } else { lists += `${md}\n`; } } else if (state === LIST_STATE && !listMatch) { state = NEUTRAL_STATE; mdArray.push(lists) lists = ''; // è¤æ°ã®ãªã¹ãããã£ãå ´åã®ãããªã¹ãå¤æ°ããªã»ãããã } if (lists.length === 0) mdArray.push(md); }); return mdArray; };
æå¾ã«index.tsã§ããã使ãããã«ãã¾ãã
// src/index.ts import { parse } from './parser' import { generate } from './generator' import { analize } from './lexer' const convertToHTMLString = (markdown: string) => { const mdArray = analize(markdown); const asts = mdArray.map(md => parse(md)); const htmlString = generate(asts) return htmlString }
ãã¦ãããã§å®æãã¾ãããsrc/index.tsã«console.log(convertToHTMLString('* list1\n* list2'))
ã追è¨ãã¦yarn run exec
ãå®è¡ãã¾ãã
12.4 ãªã¹ãã¨å¤ªåã®çµã¿åãã
ã¤ãã§ã«ãªã¹ãã¨å¤ªåã®çµã¿åããã試ãã¦ã¿ã¾ããããå®è£ ã¯ããã¾ã§ã§å®äºãã¦ãã¾ãã
normal text * **boldlist1** * list2
ãã®å ´å以ä¸ã®HTMLãåºåãããã®ãæå¾ å¤ã§ãã
normal text <ul> <li><strong>boldlist1</strong></li> <li>list2</li> </ul>
ã§ã¯src/index.tsã«console.log(convertToHTMLString('normal text\n \n * **boldlist1**\n * list2'))
ã追è¨ãã¦yarn run exec
ãå®è¡ãã¾ãã
ç¡äºã«æå¾ ããHTMLãåºåããã¾ãããããã§ãªã¹ãã®å®è£ ã¯çµããã«ãªãã¾ããã
13. çµããã«
ãã®è¨äºã§ä½ã£ãminuteãã¼ã¯ãã¦ã³ãã¼ãµã®å®å ¨çã¯GitHubã§å ¬éããã¦ãã¾ããã¾ãminuteã使ãããã¬ã¤ã°ã©ã¦ã³ããæºåãã¦ãã¾ãã
ãã®è¨äºã¯ç§ãå¼ç¤¾ã®ããã¯ãã¼ã¯ã§è©±ããå 容ããã¼ã¹ã«ãã¦ãã¾ããå¦çç³»ã®å®è£ ãåãã¦ã ã£ããã¨ããããããã¯ãã¼ã¯æã¯ææ¢ãã§å®è£ ããå 容ã調ã¹ãããã«ãªãã¨ãªãçºè¡¨ãã¾ãã(ä»è³æãè¦è¿ãã¨éåæããé¨åãå¤ã)ãããããã®ããã¯ããã°ãæ¸ãã«ããã£ã¦ã¯çãå¤ããè¨è¿°ããªãã¹ãé¿ããããããã£ããã¨ä½åãã®æè¡æ¸ãèªã¿ç解ãæ·±ããªããå·çãã¦ããã¾ãããããã§ç§ãåèã«ãã4åã®æ¬ãç´¹ä»ãã¦ããã¾ãã
- ã³ã³ãã¤ã© (æ°ã³ã³ãã¥ã¼ã¿ãµã¤ã¨ã³ã¹è¬åº§) | ä¸ç° è²ç·
- Goè¨èªã§ã¤ããã¤ã³ã¿ããªã¿ | Thorsten Ball, è¨æ¨ æ´ç¾
- ä½ã¬ãã«ããã°ã©ãã³ã° | Igor Zhirkov, åå· é¦å¤«
- è¨ç®çè«ã®åºç¤ [åè第2ç] 1.ãªã¼ãããã³ã¨è¨èª | Michael Sipser
We are hiring!
ã¨ã ã¹ãªã¼ã§ã¯å»çç¾å ´ã®ãæ¸ããããµãã¼ãããã人ã絶è³åéä¸ã§ãããå¦çç³»ã®è©±ããããããã¡ãã£ã¨è©±ãèãã¦ã¿ããããããã¨ã ã¹ãªã¼ã£ã¦å è³ï¼å¤è³ï¼ããªã©ãªã©ã«ã¸ã¥ã¢ã«ã«è©±ããã¦ã¿ããæ¹ã¯ä»¥ä¸ã®ãªã³ã¯ããå¿åãã¦ãã ããï¼
*1:ã¡ãªã¿ã«çè ã¯ä»¥åBoostnoteã¨ãããã¼ã¯ãã¦ã³ã¡ã¢ã¢ããªã®ã³ããã¿ã¼ã¨ãã¦æ´»åãã¦ãã¾ããã