/* XOWA: the XOWA Offline Wiki Application Copyright (C) 2012 gnosygnu@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ package gplx.php; import gplx.*; /* NOTE: naive implementation of PHP evaluator. intended only for parsing Messages**.php files in MediaWiki. Specifically, it assumes the following: - all lines are assignment lines: EX: $a = b; - only the assignment operator is allowed (=); EX: $a = 5 + 7; fails b/c of + operator; - no functions are supported: EX: strlen('a') fails */ public class Php_evaluator implements Php_tkn_wkr { byte mode = Mode_key_bgn, next_tid = 0, next_mode = 0; Php_line_assign cur_line; Php_itm_ary cur_ary; Php_key cur_kv_key; ListAdp frame_stack = ListAdp_.new_(); public Php_evaluator(Gfo_msg_log msg_log) {this.msg_log = msg_log;} Gfo_msg_log msg_log; public void Init(Php_ctx ctx) {src = ctx.Src(); frame_stack.Clear();} private byte[] src; public ListAdp List() {return lines;} ListAdp lines = ListAdp_.new_(); public Gfo_msg_log Msg_log() {return msg_log;} public void Clear() { lines.Clear(); msg_log.Clear(); cur_line = null; cur_ary = null; cur_kv_key = null; mode = Mode_key_bgn; next_tid = next_mode = 0; } public void Process(Php_tkn tkn) { byte tkn_tid = tkn.Tkn_tid(); switch (tkn_tid) { case Php_tkn_.Tid_declaration: case Php_tkn_.Tid_comment: case Php_tkn_.Tid_ws: // always discard, regardless of mode return; } switch (mode) { case Mode_expect: // handles sequences like "array(" if (tkn_tid == next_tid) mode = next_mode; else { Msg_many(src, tkn.Src_bgn(), tkn.Src_end(), Expecting_itm_failed, Php_tkn_.Xto_str(next_tid), Php_tkn_.Xto_str(tkn_tid)); Fail(); } break; case Mode_suspend: if (tkn_tid == Php_tkn_.Tid_semic) mode = Mode_key_bgn; break; case Mode_key_bgn: if (tkn_tid == Php_tkn_.Tid_var) { cur_ary = null; cur_line = new Php_line_assign(); lines.Add(cur_line); Php_tkn_var var_tkn = (Php_tkn_var)tkn; cur_line.Key_(new Php_itm_var(var_tkn.Var_name(src))); mode = Mode_key_end; } else { Msg_many(src, tkn.Src_bgn(), tkn.Src_end(), Expecting_itm_failed, Php_tkn_.Xto_str(Php_tkn_.Tid_var), Php_tkn_.Xto_str(tkn_tid)); Fail(); } break; case Mode_key_end: switch (tkn_tid) { case Php_tkn_.Tid_eq: mode = Mode_val; break; case Php_tkn_.Tid_brack_bgn: mode = Mode_brack_itm; break; case Php_tkn_.Tid_brack_end: Expect(Php_tkn_.Tid_eq, Mode_val); break; default: { Msg_many(src, tkn.Src_bgn(), tkn.Src_end(), Expecting_itm_failed, Php_tkn_.Xto_str(Php_tkn_.Tid_var), Php_tkn_.Xto_str(tkn_tid)); Fail(); break; } } break; case Mode_brack_itm: switch (tkn_tid) { case Php_tkn_.Tid_quote: Php_tkn_quote tkn_quote = (Php_tkn_quote)tkn; Php_itm_quote key_sub = new Php_itm_quote(tkn_quote.Quote_text(src)); cur_line.Key_subs_(new Php_key[] {key_sub}); mode = Mode_key_end; break; default: { Msg_many(src, tkn.Src_bgn(), tkn.Src_end(), Expecting_itm_failed, Php_tkn_.Xto_str(Php_tkn_.Tid_var), Php_tkn_.Xto_str(tkn_tid)); Fail(); break; } } break; case Mode_val: Php_itm line_val = null; switch (tkn_tid) { case Php_tkn_.Tid_null: Expect(Php_tkn_.Tid_semic, Mode_key_bgn); line_val = Php_itm_null._; break; case Php_tkn_.Tid_false: Expect(Php_tkn_.Tid_semic, Mode_key_bgn); line_val = Php_itm_bool_false._; break; case Php_tkn_.Tid_true: Expect(Php_tkn_.Tid_semic, Mode_key_bgn); line_val = Php_itm_bool_true._; break; case Php_tkn_.Tid_quote: Expect(Php_tkn_.Tid_semic, Mode_key_bgn); Php_tkn_quote tkn_quote = (Php_tkn_quote)tkn; line_val = new Php_itm_quote(tkn_quote.Quote_text(src)); break; case Php_tkn_.Tid_ary: Expect(Php_tkn_.Tid_paren_bgn, Mode_ary_subs); Php_itm_ary ary = new Php_itm_ary(); if (cur_ary == null) line_val = ary; else { cur_ary.Subs_add(ary); frame_stack.Add(new Php_scanner_frame(cur_ary)); cur_kv_key = null; } this.cur_ary = ary; break; case Php_tkn_.Tid_txt: case Php_tkn_.Tid_var: break; case Php_tkn_.Tid_eq: case Php_tkn_.Tid_eq_kv: case Php_tkn_.Tid_semic: case Php_tkn_.Tid_comma: case Php_tkn_.Tid_paren_bgn: case Php_tkn_.Tid_paren_end: case Php_tkn_.Tid_num: break; } cur_line.Val_(line_val); break; case Mode_ary_subs: switch (tkn_tid) { case Php_tkn_.Tid_null: Ary_add_itm(Php_itm_null._); break; case Php_tkn_.Tid_false: Ary_add_itm(Php_itm_bool_false._); break; case Php_tkn_.Tid_true: Ary_add_itm(Php_itm_bool_true._); break; case Php_tkn_.Tid_quote: Php_tkn_quote tkn_quote = (Php_tkn_quote)tkn; Ary_add_itm(new Php_itm_quote(tkn_quote.Quote_text(src))); break; case Php_tkn_.Tid_num: Php_tkn_num tkn_num = (Php_tkn_num)tkn; Ary_add_itm(new Php_itm_int(tkn_num.Num_val_int(src))); break; case Php_tkn_.Tid_var: Php_tkn_var tkn_var = (Php_tkn_var)tkn; Ary_add_itm(new Php_itm_var(Bry_.Mid(src, tkn_var.Src_bgn(), tkn_var.Src_end()))); break; case Php_tkn_.Tid_txt: Php_tkn_txt tkn_txt = (Php_tkn_txt)tkn; Ary_add_itm(new Php_itm_var(Bry_.Mid(src, tkn_txt.Src_bgn(), tkn_txt.Src_end()))); break; case Php_tkn_.Tid_ary: Expect(Php_tkn_.Tid_paren_bgn, Mode_ary_subs); Php_itm_ary ary = new Php_itm_ary(); if (cur_ary == null) line_val = ary; else { frame_stack.Add(new Php_scanner_frame(cur_ary)); if (cur_kv_key == null) cur_ary.Subs_add(ary); else { Php_itm_kv ary_itm = new Php_itm_kv().Key_(cur_kv_key).Val_(ary); cur_ary.Subs_add(ary_itm); cur_kv_key = null; } } this.cur_ary = ary; break; case Php_tkn_.Tid_paren_end: mode = Mode_ary_term; if (frame_stack.Count() == 0) cur_ary = null; else { Php_scanner_frame frame = (Php_scanner_frame)ListAdp_.Pop(frame_stack); cur_ary = frame.Ary(); frame.Rls(); } break; case Php_tkn_.Tid_semic: // NOTE: will occur in following construct array(array()); mode = Mode_key_bgn; break; case Php_tkn_.Tid_eq: case Php_tkn_.Tid_eq_kv: case Php_tkn_.Tid_comma: case Php_tkn_.Tid_paren_bgn: break; } break; case Mode_ary_dlm: switch (tkn_tid) { case Php_tkn_.Tid_comma: mode = Mode_ary_subs; break; case Php_tkn_.Tid_paren_end: mode = Mode_ary_term; if (frame_stack.Count() == 0) cur_ary = null; else { Php_scanner_frame frame = (Php_scanner_frame)ListAdp_.Pop(frame_stack); cur_ary = frame.Ary(); frame.Rls(); } break; case Php_tkn_.Tid_eq_kv: Php_itm_sub tmp_key = cur_ary.Subs_pop(); cur_kv_key = (Php_key)tmp_key; mode = Mode_ary_subs; break; } break; case Mode_ary_term: switch (tkn_tid) { case Php_tkn_.Tid_comma: case Php_tkn_.Tid_paren_end: // NOTE: paren_end occurs in multiple nests; EX: array(array()) mode = Mode_ary_subs; break; case Php_tkn_.Tid_semic: mode = Mode_key_bgn; break; } break; } } private void Fail() {mode = Mode_suspend;} private void Ary_add_itm(Php_itm val) { mode = Mode_ary_dlm; if (cur_kv_key == null) cur_ary.Subs_add((Php_itm_sub)val); else { Php_itm_kv ary_itm = new Php_itm_kv().Key_(cur_kv_key).Val_(val); cur_ary.Subs_add(ary_itm); cur_kv_key = null; } } private void Expect(byte next_tid, byte next_mode) { mode = Mode_expect; this.next_tid = next_tid; this.next_mode = next_mode; } public void Msg_many(byte[] src, int bgn, int end, Gfo_msg_itm itm, Object... args) { msg_log.Add_itm_many(itm, src, bgn, end, args); } public static final Gfo_msg_itm Expecting_itm_failed = Gfo_msg_itm_.new_warn_(Php_parser.Log_nde, "expecting_itm_failed", "expecting_itm ~{0} but got ~{1} instead"); private static final byte Mode_key_bgn = 1, Mode_key_end = 2, Mode_expect = 3, Mode_suspend = 4, Mode_val = 5, Mode_ary_subs = 6, Mode_ary_dlm = 7, Mode_ary_term = 8, Mode_brack_itm = 9; } class Php_scanner_frame { public Php_scanner_frame(Php_itm_ary ary) {this.ary = ary;} public Php_itm_ary Ary() {return ary;} Php_itm_ary ary; public void Rls() {ary = null;} } class Php_parser_interrupt { public static final Php_parser_interrupt Char = new Php_parser_interrupt(); }