Advertisement
lippiod

Untitled

Mar 25th, 2025 (edited)
295
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.       // match text until next special token
  36.       const start = i;
  37.       while (
  38.         i < input.length &&
  39.         !match('\n*\n') &&
  40.         !match('@sel(') &&
  41.         !match('@ed()') &&
  42.         !match(')') &&
  43.         !match('/')
  44.       ) {
  45.         i++;
  46.       }
  47.       const text = input.slice(start, i).trim();
  48.       if (text) {
  49.         push('T_TEXT', text);
  50.       }
  51.     }
  52.   }
  53.  
  54.   return tokens;
  55. }
  56.  
  57. // === PARSER ===
  58. function parse(tokens) {
  59.   let pos = 0;
  60.  
  61.   function peek(type) {
  62.     return tokens[pos]?.type === type;
  63.   }
  64.  
  65.   function consume(type) {
  66.     const token = tokens[pos];
  67.     if (!token || token.type !== type) {
  68.       throw new Error(`Expected ${type}, got ${token?.type} at position ${pos}`);
  69.     }
  70.     pos++;
  71.     return token;
  72.   }
  73.  
  74.   function parseText() {
  75.     const token = consume('T_TEXT');
  76.     return { type: 'text', value: token.value };
  77.   }
  78.  
  79.   function parseEdit() {
  80.     consume('T_EDIT');
  81.     return { type: 'edit' };
  82.   }
  83.  
  84.   function parseDropdown() {
  85.     consume('T_SEL_OPEN');
  86.  
  87.     const options = [];
  88.     options.push(consume('T_TEXT').value);
  89.  
  90.     while (peek('T_SLASH')) {
  91.       consume('T_SLASH');
  92.       options.push(consume('T_TEXT').value);
  93.     }
  94.  
  95.     consume('T_PAREN_CLOSE');
  96.  
  97.     return { type: 'dropdown', options };
  98.   }
  99.  
  100.   function parseE() {
  101.     if (peek('T_SEL_OPEN')) return parseDropdown();
  102.     if (peek('T_EDIT')) return parseEdit();
  103.     if (peek('T_TEXT')) return parseText();
  104.     throw new Error(`Unexpected token at position ${pos}: ${tokens[pos]?.type}`);
  105.   }
  106.  
  107.   function parseExpr() {
  108.     const elements = [];
  109.     while (
  110.       peek('T_TEXT') ||
  111.       peek('T_EDIT') ||
  112.       peek('T_SEL_OPEN')
  113.     ) {
  114.       elements.push(parseE());
  115.     }
  116.     return elements;
  117.   }
  118.  
  119.   function parseCl() {
  120.     consume('T_CK');
  121.     return parseExpr(); // Return array of e nodes directly
  122.   }
  123.  
  124.   function parseProgram() {
  125.     const result = [];
  126.     while (peek('T_CK')) {
  127.       result.push(parseCl());
  128.     }
  129.     return result;
  130.   }
  131.  
  132.   return parseProgram();
  133. }
  134.  
  135. const input = `
  136. *
  137. 今天我感覺 @sel(開心/普通/沮喪),也許是因為我終於完成了那個專案 @ed(),雖然還有一些小地方要修正 @ed()
  138. Today I felt @sel(happy/neutral/down), maybe because I finally finished that project @ed(), although a few things still need fixing @ed().
  139. *
  140.  
  141. 我選擇了一本新書 @sel(小說/歷史/自我成長) 開始閱讀,名字叫 @ed(),它讓我重新思考一些事情 @ed()
  142. 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().
  143. *
  144.  
  145. 晚餐我做了 @sel(義大利麵/壽司/沙拉),因為我最近在嘗試更健康的飲食 @ed(),也開始記錄每天的熱量攝取 @ed()
  146. For dinner, I made @sel(pasta/sushi/salad), since I’m trying to eat healthier lately @ed(), and started tracking my daily calories @ed().
  147. *
  148.  
  149. 今天我和 @ed() 一起散步,天氣是 @sel(晴天/陰天/下雨),我們聊了很多最近的煩惱 @ed()
  150. 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().
  151. *
  152.  
  153. 睡前我花了一點時間做計畫,明天我打算完成 @ed(),而且希望心情是 @sel(積極/平穩/專注)
  154. Before bed, I spent some time planning — tomorrow I hope to finish @ed(), and be in a @sel(positive/calm/focused) mindset.
  155. `;
  156.  
  157. const tokens = tokenize(input);
  158. const result = parse(tokens);
  159. console.log(JSON.stringify(result));
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement