Advertisement
lippiod

report parser js

Mar 25th, 2025
297
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // === TOKENIZER ===
  2. function tokenize(input) {
  3.   const tokens = [];
  4.   let i = 0;
  5.  
  6.   function match(str) {
  7.     return input.startsWith(str, i);
  8.   }
  9.  
  10.   function advance(n) {
  11.     i += n;
  12.   }
  13.  
  14.   function push(type, value = null) {
  15.     tokens.push({ type, value: value ?? type });
  16.   }
  17.  
  18.   while (i < input.length) {
  19.     if (match('\n*\n')) {
  20.       push('T_CK', '\n*\n');
  21.       advance(3);
  22.     } else if (match('@sel(')) {
  23.       push('T_SEL_OPEN', '@sel(');
  24.       advance(5);
  25.     } else if (match('@ed()')) {
  26.       push('T_EDIT', '@ed()');
  27.       advance(6);
  28.     } else if (match(')')) {
  29.       push('T_PAREN_CLOSE', ')');
  30.       advance(1);
  31.     } else if (match('/')) {
  32.       push('T_SLASH', '/');
  33.       advance(1);
  34.     } else {
  35.       const start = i;
  36.       while (
  37.         i < input.length &&
  38.         !match('\n*\n') &&
  39.         !match('@sel(') &&
  40.         !match('@ed()') &&
  41.         !match(')') &&
  42.         !match('/')
  43.       ) {
  44.         i++;
  45.       }
  46.       const text = input.slice(start, i).trim();
  47.       if (text) {
  48.         push('T_TEXT', text);
  49.       }
  50.     }
  51.   }
  52.  
  53.   return tokens;
  54. }
  55.  
  56. // === PARSER ===
  57. function parse(tokens) {
  58.   let pos = 0;
  59.   let clCounter = 1;
  60.   let eCounter = 1;
  61.  
  62.   function peek(type) {
  63.     return tokens[pos]?.type === type;
  64.   }
  65.  
  66.   function consume(type) {
  67.     const token = tokens[pos];
  68.     if (!token || token.type !== type) {
  69.       throw new Error(`Expected ${type}, got ${token?.type} at position ${pos}`);
  70.     }
  71.     pos++;
  72.     return token;
  73.   }
  74.  
  75.   function parseText() {
  76.     const token = consume('T_TEXT');
  77.     return { type: 'text', value: token.value };
  78.   }
  79.  
  80.   function parseEdit() {
  81.     consume('T_EDIT');
  82.     return { type: 'edit', id: `e-${eCounter++}` };
  83.   }
  84.  
  85.   function parseDropdown() {
  86.     consume('T_SEL_OPEN');
  87.  
  88.     const options = [];
  89.     options.push(consume('T_TEXT').value);
  90.  
  91.     while (peek('T_SLASH')) {
  92.       consume('T_SLASH');
  93.       options.push(consume('T_TEXT').value);
  94.     }
  95.  
  96.     consume('T_PAREN_CLOSE');
  97.  
  98.     return { type: 'dropdown', id: `e-${eCounter++}`, options };
  99.   }
  100.  
  101.   function parseE() {
  102.     if (peek('T_SEL_OPEN')) return parseDropdown();
  103.     if (peek('T_EDIT')) return parseEdit();
  104.     if (peek('T_TEXT')) return parseText();
  105.     throw new Error(`Unexpected token at position ${pos}: ${tokens[pos]?.type}`);
  106.   }
  107.  
  108.   function parseExpr() {
  109.     const elements = [];
  110.     while (
  111.       peek('T_TEXT') ||
  112.       peek('T_EDIT') ||
  113.       peek('T_SEL_OPEN')
  114.     ) {
  115.       elements.push(parseE());
  116.     }
  117.     return elements;
  118.   }
  119.  
  120.   function parseCl() {
  121.     consume('T_CK');
  122.     const items = parseExpr();
  123.     return {
  124.       id: `cl-${clCounter++}`,
  125.       checked: false,
  126.       items
  127.     };
  128.   }
  129.  
  130.   function parseProgram() {
  131.     const result = [];
  132.     while (peek('T_CK')) {
  133.       result.push(parseCl());
  134.     }
  135.     return result;
  136.   }
  137.  
  138.   return parseProgram();
  139. }
  140.  
  141. // === EXAMPLE INPUT (Diary-style, bilingual) ===
  142. const input = `
  143. *
  144. 今天我感覺 @sel(開心/普通/沮喪),也許是因為我終於完成了那個專案 @ed(),雖然還有一些小地方要修正 @ed()
  145. Today I felt @sel(happy/neutral/down), maybe because I finally finished that project @ed(), although a few things still need fixing @ed()
  146. *
  147. 我選擇了一本新書 @sel(小說/歷史/自我成長) 開始閱讀,名字叫 @ed(),它讓我重新思考一些事情 @ed()
  148. I picked up a new book @sel(fiction/history/self-help) to read, titled @ed(), and it made me reflect on a few things @ed()
  149. *
  150. 晚餐我做了 @sel(義大利麵/壽司/沙拉),因為我最近在嘗試更健康的飲食 @ed(),也開始記錄每天的熱量攝取 @ed()
  151. For dinner, I made @sel(pasta/sushi/salad), since I’m trying to eat healthier lately @ed(), and started tracking my daily calories @ed()
  152. *
  153. 今天我和 @ed() 一起散步,天氣是 @sel(晴天/陰天/下雨),我們聊了很多最近的煩惱 @ed()
  154. I went for a walk with @ed() today — the weather was @sel(sunny/cloudy/rainy), and we talked about a lot of recent worries @ed()
  155. *
  156. 睡前我花了一點時間做計畫,明天我打算完成 @ed(),而且希望心情是 @sel(積極/平穩/專注)
  157. Before bed, I spent some time planning — tomorrow I hope to finish @ed(), and be in a @sel(positive/calm/focused) mindset。
  158. `;
  159.  
  160. // === USAGE ===
  161. const tokens = tokenize(input);
  162. const result = parse(tokens);
  163. console.log(JSON.stringify(result, null, 2));
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement