1 module dlex.DLex;
2 
3 import std.conv;
4 import std.array;
5 import std.typecons;
6 import std.algorithm;
7 
8 import dlex.Position;
9 import dlex.MatchResult;
10 import dlex.Rule;
11 
12 template DLex(Type) {
13     class DLex {
14 	class LexResult {
15 	    public:
16 		Type type;
17 		dstring str;
18 		Position pos;
19 
20 		this (Type type, dstring str, Position pos) {
21 		    this.type = type;
22 		    this.str = str;
23 		    this.pos = pos;
24 		}
25 	}
26 	public:
27 	    struct RuleT {
28 		Type type;
29 		Rule rule;
30 	    }
31 
32 	    RuleT[] rules;
33 
34 	    void Rules(RuleT[] rules) {
35 		this.rules = rules;
36 	    }
37 
38 	    auto Lex(dstring source) {
39 		Position pos;
40 		LexResult[] results = [];
41 
42 		struct Matched {
43 		    MatchResult result;
44 		    Position pos;
45 		    Type type;
46 		    bool skip;
47 		}
48 		while (!pos.end(source)) {
49 		    Matched[] rs = [];
50 		    foreach (rule; rules) {
51 			auto savePos = pos;
52 			MatchResult r = rule.rule.matched(source, savePos);
53 			if (r && r.str.length > 0) { // 0文字でマッチし得る
54 			    rs ~= Matched(r, savePos, rule.type, rule.rule.skip); 
55 			}
56 		    }
57 		    if (rs.length == 0) {
58 			break;
59 		    }
60 
61 
62 		    // 最長一致なので最長のMatchResultを採用する
63 		    // 同じ長さならより早く見つけた(先に登録した)方を優先する
64 		    auto result = rs.reduce!((a,b) => (
65 				(a.result.str.length >= b.result.str.length) ? a : b
66 				));
67 
68 		    if (!result.skip) {
69 			results ~= new LexResult(result.type, result.result.str, pos);
70 		    }
71 		    pos = result.pos;
72 		}
73 		if (!pos.end(source)) {
74 		    throw new Exception("Lex rules did not fully matched input string");
75 		}
76 
77 		return results;
78 	    }
79     }
80 }
81