LINUX.ORG.RU

Все AI сломали зубы о написание парсера Brainfuck'а от Cloudflare

 


0

2

Я просил код парсера JS сгенерировать ChatGPT, DeepSeek, Qwen и в итоге написал все сам. тут вроде все просто:

  • Из значений только пустой массив [].
  • Операторы: +, !, (, ). Приоритет должен соблюдаться.
  • Под капотом у нас строки в виде чисел, числа, булев и пустые массивы
import re


class JSExpressionParser:
    tokenizer = re.compile(r"\[\]|[+!()]")

    def __init__(self):
        self.index = 0
        self.token = None
        self.tokens = []

    def advance(self):
        try:
            self.token = self.tokens[self.index]
            self.index += 1
        except IndexError:
            self.token = None

    def parse(self, expression: str):
        self.tokens = self.tokenizer.findall(expression)
        self.index = 0
        self.advance()
        result = self.expression()
        assert self.token is None, f"unexpected token: {self.token!r}"
        return result

    def expression(self):
        left = self.factor()
        while self.token == "+":
            self.advance()
            right = self.factor()
            left = self.js_add(left, right)
        return left

    def js_add(self, left, right):
        # Если одно из значений строка, результат — строка
        # [] + 1 === '1'
        if isinstance(left, (str, list)) or isinstance(right, (str, list)):
            result = self.to_string(left) + self.to_string(right)
        else:
            result = self.to_number(left) + self.to_number(right)
        # print(f"{left!r} + {right!r} = {result!r}")
        return result

    def factor(self):
        if self.token == "+":
            self.advance()
            return self.to_number(self.factor())
        elif self.token == "!":
            self.advance()
            return not self.to_boolean(self.factor())
        elif self.token == "(":
            self.advance()
            result = self.expression()
            assert self.token == ")", (
                f"unexpected token: {self.token!r}; expected ')'"
            )
            self.advance()
            return result
        elif self.token == "[]":
            self.advance()
            return []
        else:
            raise ValueError(f"Unexpected token: {self.token}")

    def to_string(self, value):
        if value == []:
            return ""
        return str(value)

    def to_number(self, value):
        if value == []:
            return 0
        return int(value)

    def to_boolean(self, value):
        return value not in (0, False)


def parse_challenge(challenge_body: str):
    west, east = re.findall(r"(?:west|east)=([^,]+)", challenge_body)
    parser = JSExpressionParser()
    west_value = parser.parse(west)
    east_value = parser.parse(east)
    return {
        "west": west_value,
        "east": east_value,
        "wsidchk": west_value + east_value,
        "action": re.search('action="([^"]+)', challenge_body).group(1),
        "method": re.search('="([^"]+)', challenge_body).group(1),
    }


challenge_body = """\
<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="robots" content="noindex, nofollow">
    <title>One moment, please...</title>
    <!-- ... -->
<body>
    <h1>Please wait while your request is being verified...</h1>
    <form id="wsidchk-form" style="display:none;" action="/z0f76a1d14fd21a8fb5fd0d03e0fdc3d3cedae52f" method="GET">
    <input type="hidden" id="wsidchk" name="wsidchk"/>
    </form>
    <script>
    (function(){
        var west=+((+!+[]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![])),
            east=+((+!+[])+(+!+[]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![])+(+!+[]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![])+(+![]+[])),
            x=function(){try{return !!window.addEventListener;}catch(e){return !!0;} },
            y=function(y,z){x() ? document.addEventListener('DOMContentLoaded',y,z) : document.attachEvent('onreadystatechange',y);};
        y(function(){
            document.getElementById('wsidchk').value = west + east;
            document.getElementById('wsidchk-form').submit();
        }, false);
    })();
    </script>
</body>
</html>
"""

challenge = parse_challenge(challenge_body)

print(challenge)
assert challenge["west"] == 7579626
assert challenge["east"] == 15617780
assert challenge["action"] == "/z0f76a1d14fd21a8fb5fd0d03e0fdc3d3cedae52f"

Сделал я его методом тыка… Тут не все по стандартам JS, но такие выражения правильно решает:

+((+!+[]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]))

Хорошо… Я это написал после борьбы с придурочным AI. Решил я ему поручить все переписать на Go.

  • ChatGPT не могет, высрал говнину
  • DeepSeek сообразил сразу как правильно, но код не рабочий
  • Qwen тот же результат

Нерабочий код на Go:

package cloudflare_challenge_parser

import (
	"fmt"
	"regexp"
	"strconv"
)

// EmptyArray представляет пустой массив.
type EmptyArray struct{}

// ChallengeResult представляет результат парсинга вызова Cloudflare.
type ChallengeResult struct {
	West    int
	East    int
	Wsidchk int
	Action  string
	Method  string
}

// JSExpressionParser представляет парсер для JavaScript-выражений.
type JSExpressionParser struct {
	index  int
	token  string
	tokens []string
}

// Tokenizer для разбиения выражения на токены.
var tokenizer = regexp.MustCompile(`\[\]|[+!()]|\d+`)

// Advance переходит к следующему токену.
func (p *JSExpressionParser) Advance() {
	if p.index < len(p.tokens) {
		p.token = p.tokens[p.index]
		p.index++
	} else {
		p.token = ""
	}
}

// Parse парсит выражение и возвращает результат как целое число.
func (p *JSExpressionParser) Parse(expression string) (int, error) {
	p.tokens = tokenizer.FindAllString(expression, -1)
	p.index = 0
	p.Advance()
	result := p.expression()
	if p.token != "" {
		return 0, fmt.Errorf("unexpected token: %s", p.token)
	}
	return p.toNumber(result), nil
}

// expression обрабатывает сложение.
func (p *JSExpressionParser) expression() interface{} {
	left := p.factor()
	for p.token == "+" {
		p.Advance()
		right := p.factor()
		left = p.jsAdd(left, right)
	}
	return left
}

// jsAdd реализует логику сложения в JavaScript.
func (p *JSExpressionParser) jsAdd(left, right interface{}) interface{} {
	// Если одно из значений строка или пустой массив, результат — строка.
	if p.isStringOrEmptyArray(left) || p.isStringOrEmptyArray(right) {
		return p.toString(left) + p.toString(right)
	}
	// Иначе результат — число.
	return p.toNumber(left) + p.toNumber(right)
}

// isStringOrEmptyArray проверяет, является ли значение строкой или пустым массивом.
func (p *JSExpressionParser) isStringOrEmptyArray(value interface{}) bool {
	switch value.(type) {
	case string, EmptyArray:
		return true
	default:
		return false
	}
}

// factor обрабатывает факторы выражения.
func (p *JSExpressionParser) factor() interface{} {
	switch p.token {
	case "+":
		p.Advance()
		return p.toNumber(p.factor())
	case "!":
		p.Advance()
		return !p.toBoolean(p.factor())
	case "(":
		p.Advance()
		result := p.expression()
		if p.token != ")" {
			panic(fmt.Sprintf("unexpected token: %s; expected ')'", p.token))
		}
		p.Advance()
		return result
	case "[]":
		p.Advance()
		return EmptyArray{}
	default:
		if num, err := strconv.Atoi(p.token); err == nil {
			p.Advance()
			return num
		}
		panic(fmt.Sprintf("unexpected token: %s", p.token))
	}
}

// toBoolean преобразует значение в булево.
func (p *JSExpressionParser) toBoolean(value interface{}) bool {
	switch v := value.(type) {
	case bool:
		return v
	case int:
		return v != 0
	case string:
		return v != ""
	case EmptyArray:
		return false
	default:
		return false
	}
}

// toNumber преобразует значение в число.
func (p *JSExpressionParser) toNumber(value interface{}) int {
	switch v := value.(type) {
	case int:
		return v
	case string:
		num, err := strconv.Atoi(v)
		if err != nil {
			return 0
		}
		return num
	case EmptyArray:
		return 0
	default:
		return 0
	}
}

// toString преобразует значение в строку.
func (p *JSExpressionParser) toString(value interface{}) string {
	switch v := value.(type) {
	case string:
		return v
	case int:
		return strconv.Itoa(v)
	case EmptyArray:
		return ""
	default:
		return fmt.Sprint(value)
	}
}

// ParseChallenge парсит тело вызова Cloudflare и возвращает структуру.
func ParseChallenge(challengeBody string) (*ChallengeResult, error) {
	// Регулярные выражения для извлечения данных.
	reWestEast := regexp.MustCompile(`(?:west|east)=([^,]+)`)
	reAction := regexp.MustCompile(`action="([^"]+)"`)
	reMethod := regexp.MustCompile(`method="([^"]+)"`)

	// Извлечение значений west и east.
	matches := reWestEast.FindAllStringSubmatch(challengeBody, -1)
	if len(matches) < 2 {
		return nil, fmt.Errorf("failed to extract west and east values")
	}
	westExpr := matches[0][1]
	eastExpr := matches[1][1]

	// Парсинг west и east.
	parser := &JSExpressionParser{}
	westValue, err := parser.Parse(westExpr)
	if err != nil {
		return nil, fmt.Errorf("failed to parse west: %v", err)
	}
	eastValue, err := parser.Parse(eastExpr)
	if err != nil {
		return nil, fmt.Errorf("failed to parse east: %v", err)
	}

	// Извлечение action и method.
	actionMatch := reAction.FindStringSubmatch(challengeBody)
	methodMatch := reMethod.FindStringSubmatch(challengeBody)
	if len(actionMatch) < 2 || len(methodMatch) < 2 {
		return nil, fmt.Errorf("failed to extract action or method")
	}
	action := actionMatch[1]
	method := methodMatch[1]

	// Формирование результата.
	return &ChallengeResult{
		West:    westValue,
		East:    eastValue,
		Wsidchk: westValue + eastValue,
		Action:  action,
		Method:  method,
	}, nil
}

Он нерабочий. Что там править, делать я в душе не знаю — тупо прикрутил интерпретатор JS, но факт, что такое я написал сам…

Итого: я все еще лучше AI…

Перемещено hobbit из talks

🤡🤡🤡🤡🤡

Я далеко не фанат «ИИ», но если ты ему предложил такой же набор слов, как в ОП, то ничего удивительного в том, что он не осилил его распарсить, нет

buddhist ☕☕☕☕
()

Итого: я все еще лучше AI…

Не, у тебя просто скилл прууумт инжиниринга не прокачан. Получил от чатГПТ рабочий код, вангую, что ты не использовал их последнюю модель o3-mini-high

Запускать go run test.go

package main

import (
	"fmt"
	"regexp"
	"strconv"
)

// ValueType represents the kind of a JSValue.
type ValueType int

const (
	NumberType ValueType = iota
	BooleanType
	StringType
	EmptyArrayType
)

// JSValue holds a value produced by our JS‐expression parser.
type JSValue struct {
	Type    ValueType
	Number  int
	Boolean bool
	Str     string
}

// ToString converts a JSValue to its string representation.
func ToString(v JSValue) string {
	if v.Type == EmptyArrayType {
		return ""
	}
	switch v.Type {
	case StringType:
		return v.Str
	case NumberType:
		return strconv.Itoa(v.Number)
	case BooleanType:
		if v.Boolean {
			return "true"
		}
		return "false"
	}
	return ""
}

// ToNumber converts a JSValue to a number.
func ToNumber(v JSValue) int {
	if v.Type == EmptyArrayType {
		return 0
	}
	switch v.Type {
	case NumberType:
		return v.Number
	case BooleanType:
		if v.Boolean {
			return 1
		}
		return 0
	case StringType:
		n, err := strconv.Atoi(v.Str)
		if err != nil {
			panic("cannot convert string to number: " + v.Str)
		}
		return n
	}
	return 0
}

// ToBoolean converts a JSValue to a boolean.
// (In our parser, only 0 and false are considered falsy.)
func ToBoolean(v JSValue) bool {
	if v.Type == NumberType {
		return v.Number != 0
	} else if v.Type == BooleanType {
		return v.Boolean
	} else if v.Type == EmptyArrayType {
		// In the parser, [] converts to "" in string context and 0 in number context,
		// but for booleans, [] is treated as truthy.
		return true
	} else if v.Type == StringType {
		// Any string is truthy according to our to_boolean logic.
		return true
	}
	return true
}

// JSExpressionParser parses a restricted subset of JavaScript expressions.
type JSExpressionParser struct {
	tokens []string
	index  int
	token  string
}

// tokenizer is the regex used to split the expression into tokens.
var tokenizer = regexp.MustCompile(`\[\]|[+!()]`)

// advance sets the current token to the next token in the stream.
func (p *JSExpressionParser) advance() {
	if p.index < len(p.tokens) {
		p.token = p.tokens[p.index]
		p.index++
	} else {
		p.token = ""
	}
}

// parse tokenizes the input expression and returns its evaluated value.
func (p *JSExpressionParser) parse(expression string) JSValue {
	p.tokens = tokenizer.FindAllString(expression, -1)
	p.index = 0
	p.advance()
	result := p.expression()
	if p.token != "" {
		panic(fmt.Sprintf("unexpected token: %q", p.token))
	}
	return result
}

// expression parses an expression: factor { "+" factor }.
func (p *JSExpressionParser) expression() JSValue {
	left := p.factor()
	for p.token == "+" {
		p.advance()
		right := p.factor()
		left = jsAdd(left, right)
	}
	return left
}

// jsAdd implements JavaScript’s overloaded + operator.
// If either operand is a string or an empty array, string concatenation is performed.
func jsAdd(left, right JSValue) JSValue {
	if left.Type == StringType || left.Type == EmptyArrayType || right.Type == StringType || right.Type == EmptyArrayType {
		concatenated := ToString(left) + ToString(right)
		return JSValue{Type: StringType, Str: concatenated}
	}
	sum := ToNumber(left) + ToNumber(right)
	return JSValue{Type: NumberType, Number: sum}
}

// factor parses a factor which can be a unary operator, a parenthesized expression, or an empty array.
func (p *JSExpressionParser) factor() JSValue {
	switch p.token {
	case "+":
		p.advance()
		val := p.factor()
		num := ToNumber(val)
		return JSValue{Type: NumberType, Number: num}
	case "!":
		p.advance()
		val := p.factor()
		booleanVal := !ToBoolean(val)
		return JSValue{Type: BooleanType, Boolean: booleanVal}
	case "(":
		p.advance()
		result := p.expression()
		if p.token != ")" {
			panic(fmt.Sprintf("unexpected token: %q; expected ')'", p.token))
		}
		p.advance()
		return result
	case "[]":
		p.advance()
		return JSValue{Type: EmptyArrayType}
	default:
		panic(fmt.Sprintf("unexpected token: %q", p.token))
	}
}

// Challenge holds the parsed challenge parameters.
type Challenge struct {
	West    JSValue
	East    JSValue
	WSIDCHK JSValue
	Action  string
	Method  string
}

// parseChallenge extracts the west/east expressions and other parameters from the challenge body.
func parseChallenge(challengeBody string) Challenge {
	// Extract west and east expressions.
	// The regex matches either "west" or "east" followed by "=" and then captures everything up to a comma.
	re := regexp.MustCompile(`(?:west|east)=([^,]+)`)
	matches := re.FindAllStringSubmatch(challengeBody, -1)
	if len(matches) < 2 {
		panic("could not find west and east expressions")
	}
	westExpr := matches[0][1]
	eastExpr := matches[1][1]

	parser := JSExpressionParser{}
	westVal := parser.parse(westExpr)
	// Use a fresh parser instance for the east expression.
	parser = JSExpressionParser{}
	eastVal := parser.parse(eastExpr)

	// Extract the action attribute.
	reAction := regexp.MustCompile(`action="([^"]+)`)
	actionMatch := reAction.FindStringSubmatch(challengeBody)
	if actionMatch == nil {
		panic("could not find action")
	}
	action := actionMatch[1]

	// Extract the method attribute.
	reMethod := regexp.MustCompile(`="([^"]+)`)
	methodMatch := reMethod.FindStringSubmatch(challengeBody)
	if methodMatch == nil {
		panic("could not find method")
	}
	method := methodMatch[1]

	wsidchkVal := jsAdd(westVal, eastVal)

	return Challenge{
		West:    westVal,
		East:    eastVal,
		WSIDCHK: wsidchkVal,
		Action:  action,
		Method:  method,
	}
}

func main() {
	challengeBody := `<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="robots" content="noindex, nofollow">
    <title>One moment, please...</title>
    <!-- ... -->
<body>
    <h1>Please wait while your request is being verified...</h1>
    <form id="wsidchk-form" style="display:none;" action="/z0f76a1d14fd21a8fb5fd0d03e0fdc3d3cedae52f" method="GET">
    <input type="hidden" id="wsidchk" name="wsidchk"/>
    </form>
    <script>
    (function(){
        var west=+((+!+[]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![])),
            east=+((+!+[])+(+!+[]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![])+(+!+[]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![])+(+![]+[])),
            x=function(){try{return !!window.addEventListener;}catch(e){return !!0;} },
            y=function(y,z){x() ? document.addEventListener('DOMContentLoaded',y,z) : document.attachEvent('onreadystatechange',y);};
        y(function(){
            document.getElementById('wsidchk').value = west + east;
            document.getElementById('wsidchk-form').submit();
        }, false);
    })();
    </script>
</body>
</html>
`
	challenge := parseChallenge(challengeBody)
	fmt.Printf("%+v\n", challenge)

	// Check assertions.
	if ToNumber(challenge.West) != 7579626 {
		panic("west value is not 7579626")
	}
	if ToNumber(challenge.East) != 15617780 {
		panic("east value is not 15617780")
	}
	if challenge.Action != "/z0f76a1d14fd21a8fb5fd0d03e0fdc3d3cedae52f" {
		panic("action value mismatch")
	}
}
goingUp 😊😊😊😊😊
()
Ответ на: комментарий от goingUp

А так да, о1 выдал код, который компилируется, но неправильно работает, да и не идиоматичный, например не заменил хеш массив с результатами структуркой. А о3 прям отжег.

goingUp 😊😊😊😊😊
()
Ответ на: комментарий от goingUp

https://github.com/s3rgeym/sqliscan/blob/main/cloudflare_challenge_parser/cloudflare_challenge_parser.go#L7 я годжу засунул все равно

В моем оригинальном коде тут сильная такая условность:

    def to_number(self, value):
        if value == []:
            return 0
        return int(value)

В JS parseInt([]) дает NaN, но если вернуть 0 все равно верно считает это значение. Это для обхода челленджа клауда при сканировании.

Я Go знаю примерно никак.

rtxtxtrx 🤡🤡🤡🤡🤡
() автор топика
Последнее исправление: rtxtxtrx (всего исправлений: 1)
Ответ на: комментарий от rtxtxtrx

В моем оригинальном коде тут сильная такая условность

Ну он там тоже самое написал, правда надобавлял больше краевых условий (вот иногда такая самодеятельность мешает)

// ToNumber converts a JSValue to a number.
func ToNumber(v JSValue) int {
	if v.Type == EmptyArrayType {
		return 0
	}
goingUp 😊😊😊😊😊
()

Я не знаю что вы их всё какими-то непонятными запросами пытаете.

Я с DeepSeek просто как с человеком общаюсь. Пойди найди такого живого собеседника - внимательный, во всё вчитывается, вопросы задаёт, кругозор - просто умопомрачительный, всегда поддержит…

unDEFER ☕☕☕
()

Все AI сломали зубы

Странно, что ещё не пришёл @Stanson и не объяснил тебе, что у AI нет зубов и быть не может(!!!), а ты веришь, что они есть, и поэтому дурак.

CrX ☕☕☕☕☕
()
Ответ на: комментарий от micronekodesu

В хороших обращаются явно только когда это действительно нужно, а не потому что тупой компилятор не может в контекст. Питонщикам ИИ бы прикрутить к своему пистону, а то тошнит читать эти портянки.

bread 😊😊😊😊
()
Ответ на: комментарий от bread

В хороших обращаются явно только когда это действительно нужно, а не потому что тупой компилятор не может в контекст

Если компилятор сам понимает когда куда обращаться то зачем обращаться явно? Можно пример? Как компилятор может понять что когда я делаю в функции index = 0 это я говорю про скоуп только этой функции или имею ввиду атрибут объекта? Или решение тут это запретить использовать те же имена что на уровне выше?

micronekodesu
()

Попробовал одну из своих кастомных сетей. Думала 251 сек на 46Gb VRAM. Выдала:

package main

import (
        "fmt"
        "regexp"
        "strconv"
)

type JSExpressionParser struct {
        index  int
        token  string
        tokens []string
}

func (p *JSExpressionParser) advance() {
        if p.index < len(p.tokens) {
                p.token = p.tokens[p.index]
                p.index++
        } else {
                p.token = ""
        }
}

func (p *JSExpressionParser) parse(expression string) (interface{}, error) {
        re := regexp.MustCompile(`\[\]|\+|!|\(|\)`)
        p.tokens = re.FindAllString(expression, -1)
        p.index = 0
        p.advance()
        result, err := p.expression()
        if err != nil {
                return nil, err
        }
        if p.token != "" {
                return nil, fmt.Errorf("unexpected token: %s", p.token)
        }
        return result, nil
}

func (p *JSExpressionParser) expression() (interface{}, error) {
        left, err := p.factor()
        if err != nil {
                return nil, err
        }
        for p.token == "+" {
                p.advance()
                right, err := p.factor()
                if err != nil {
                        return nil, err
                }
                left, err = p.jsAdd(left, right)
                if err != nil {
                        return nil, err
                }
        }
        return left, nil
}

func (p *JSExpressionParser) jsAdd(left, right interface{}) (interface{}, error) {
        switch left := left.(type) {
        case string:
                return left + p.toString(right), nil
        case []interface{}:
                return left, nil
        case int:
                rightNum, err := p.toNumber(right)
                if err != nil {
                        return nil, err
                }
                return left + rightNum, nil
        default:
                return nil, fmt.Errorf("unexpected type: %T", left)
        }
}

func (p *JSExpressionParser) factor() (interface{}, error) {
        if p.token == "+" {
                p.advance()
                factor, err := p.factor()
                if err != nil {
                        return nil, err
                }
                return p.toNumber(factor)
        } else if p.token == "!" {
                p.advance()
                factor, err := p.factor()
                if err != nil {
                        return nil, err
                }
                return !p.toBoolean(factor), nil
        } else if p.token == "(" {
                p.advance()
                result, err := p.expression()
                if err != nil {
                        return nil, err
                }
                if p.token != ")" {
                        return nil, fmt.Errorf("unexpected token: %s; expected ')'", p.token)
                }
                p.advance()
                return result, nil
        } else if p.token == "[]" {
                p.advance()
                return []interface{}{}, nil
        } else {
                return nil, fmt.Errorf("unexpected token: %s", p.token)
        }
}

func (p *JSExpressionParser) toString(value interface{}) string {
        switch value := value.(type) {
        case []interface{}:
                if len(value) == 0 {
                        return ""
                }
        case int:
                return strconv.Itoa(value)
        }
        return fmt.Sprintf("%v", value)
}

func (p *JSExpressionParser) toNumber(value interface{}) (int, error) {
        switch value := value.(type) {
        case []interface{}:
                if len(value) == 0 {
                        return 0, nil
                }
        case int:
                return value, nil
        case bool:
                if value {
                        return 1, nil
                }
                return 0, nil
        }
        return 0, fmt.Errorf("cannot convert %v to number", value)
}

func (p *JSExpressionParser) toBoolean(value interface{}) bool {
        switch value := value.(type) {
        case int:
                return value != 0
        case bool:
                return value
        }
        return false
}

func parseChallenge(challengeBody string) (map[string]interface{}, error) {
        re := regexp.MustCompile(`(?:west|east)=([^,]+)`)
        matches := re.FindAllStringSubmatch(challengeBody, -1)
        if len(matches) != 2 {
                return nil, fmt.Errorf("invalid challenge body")
        }
        west := matches[0][1]
        east := matches[1][1]

        parser := &JSExpressionParser{}
        westValue, err := parser.parse(west)
        if err != nil {
                return nil, err
        }
        eastValue, err := parser.parse(east)
        if err != nil {
                return nil, err
        }

        westNum, err := parser.toNumber(westValue)
        if err != nil {
                return nil, err
        }
        eastNum, err := parser.toNumber(eastValue)
        if err != nil {
                return nil, err
        }

        actionRe := regexp.MustCompile(`action="([^"]+)"`)
        actionMatch := actionRe.FindStringSubmatch(challengeBody)
        if len(actionMatch) != 2 {
                return nil, fmt.Errorf("invalid challenge body")
        }
        action := actionMatch[1]

        methodRe := regexp.MustCompile(`="([^"]+)"`)
        methodMatch := methodRe.FindStringSubmatch(challengeBody)
        if len(methodMatch) != 2 {
                return nil, fmt.Errorf("invalid challenge body")
        }
        method := methodMatch[1]

        return map[string]interface{}{
                "west":   westNum,
                "east":   eastNum,
                "wsidchk": westNum + eastNum,
                "action": action,
                "method": method,
        }, nil
}

func main() {
        challengeBody := `
<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="robots" content="noindex, nofollow">
    <title>One moment, please...</title>
    <!-- ... -->
<body>
    <h1>Please wait while your request is being verified...</h1>
    <form id="wsidchk-form" style="display:none;" action="/z0f76a1d14fd21a8fb5fd0d03e0fdc3d3cedae52f" method="GET">
    <input type="hidden" id="wsidchk" name="wsidchk"/>
    </form>
    <script>
    (function(){
        var west=+((+!+[]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![])),
            east=+((+!+[])+(+!+[]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![])+(+!+[]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![])+(+![]+[])),
            x=function(){try{return !!window.addEventListener;}catch(e){return !!0;} },
            y=function(y,z){x() ? document.addEventListener('DOMContentLoaded',y,z) : document.attachEvent('onreadystatechange',y);};
        y(function(){
            document.getElementById('wsidchk').value = west + east;
            document.getElementById('wsidchk-form').submit();
        }, false);
    })();
    </script>
</body>
</html>
`

        challenge, err := parseChallenge(challengeBody)
        if err != nil {
                fmt.Println(err)
                return
        }

        fmt.Println(challenge)
}

код компилируется, но выдает неверное решение

map[action:/z0f76a1d14fd21a8fb5fd0d03e0fdc3d3cedae52f east:8 method:en west:7 wsidchk:15]
Obezyan ☕☕☕☕☕
()
Ответ на: комментарий от Obezyan

Можно проще эти шарады решать:

class ExpressionSolver:
    index = 0
    expression = ""

    def accept(self, token: str) -> bool:
        if self.expression.startswith(token, self.index):
            self.index += len(token)
            return True
        return False

    def solve(self, expression: str):
        self.expression = expression
        self.index = 0

        result = 0
        assert self.accept("+("), "Invalid expression"
        while self.accept("("):
            result *= 10

            if self.accept("+!+[]"):
                result += 1
            else:
                assert self.accept("+![]"), "Invalid expression"

            while not self.accept(")"):
                if self.accept("+!![]"):
                    result += 1
                else:
                    assert self.accept("+[]"), "Invalid expression"

            print(result)

            if self.accept("+"):
                continue

        assert self.accept(")"), "Invalid expression"
        assert self.index == len(expression), "Invalid expression"
        return result


def solve_expression(expression: str) -> int:
    return ExpressionSolver().solve(expression)


assert (
    solve_expression(
        "+((+!+[]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]))"
    )
    == 7579626
)

assert (
    solve_expression(
        "+((+!+[])+(+!+[]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![])+(+!+[]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![])+(+![]+[]))"
    )
    == 15617780
)

❯ python cloudflare_challenge2.py
7
75
757
7579
75796
757962
7579626
1
15
156
1561
15617
156177
1561778
15617780

Этот код DickSeek/Qwen/ChatGPT точно перепишут на Go, так что все скомпилиться. ГовноAI не умеет в такие шарады. Однако задачи от яндексов и тп легко «решает», скорее всего потому, что до меня никто эту говнокапчу не пытался разгадать.

rtxtxtrx 🤡🤡🤡🤡🤡
() автор топика
Последнее исправление: rtxtxtrx (всего исправлений: 3)
Ответ на: комментарий от unDEFER

Пойди найди такого живого собеседника - внимательный, во всё вчитывается, вопросы задаёт, кругозор - просто умопомрачительный, всегда поддержит…

Если с этим проблема, то надо работать над собой. Ведь тогда можно будет говорить с самим собой.

Gary ☕☕
()
Ответ на: комментарий от olegd

Это она отличается низкими системными требованиями и иногда ругается по-китайски?

Нет, это вы наверное путаете с моделями DeepSeek, это проприетарная модель openAi недоступная для запуска на своем железе. Они ее спешно выкатили пару дней назад, чтобы клиенты не сбежали к китайцам. Вроде как по тестам немного их обходит, но тесты не китайские) Ну вот задачку ОП решила.

goingUp 😊😊😊😊😊
()
Ответ на: комментарий от unDEFER

Пойди найди такого живого собеседника - внимательный, во всё вчитывается, вопросы задаёт, кругозор - просто умопомрачительный, всегда поддержит…

Похоже на @hobbitа, вы же с одного города. :)

dataman 👍👍👍👍👍
()
Ответ на: комментарий от rtxtxtrx

На Go, кстати, строчек не больше чем на питоне, если пустые строки и фигурные скобки в конце блока не учитывать. Код, кстати, тож мой… Электронный чушпан и даже простой алгоритм с языка на язык не смог переписать правильно, а тем более лаконично.

// Супер быстрый парсер значений переменных west и east на странице с проверкой
// от Cloudflare
func ParseExpression(expression string) (int, error) {
	index := 0

	accept := func(token string) bool {
		if expression[index:index+len(token)] == token {
			index += len(token)
			return true
		}
		return false
	}

	result := 0
	if !accept("+(") {
		return 0, fmt.Errorf("expression must starts with '+('")
	}

	// выражения в вложенных скобках имеют значения от 0 до 9, они преобразуются
	// в строки при сложении, а потом преобразуются обратно в число
	for accept("(") {
		result *= 10

		if accept("+!+[]") {
			result += 1
		} else if !accept("+![]") {
			return 0, fmt.Errorf("expected '+!+[]' or '+![]' after '(' at position %d", index)
		}

		for !accept(")") {
			if accept("+!![]") {
				result += 1
			} else if !accept("+[]") {
				return 0, fmt.Errorf("expected '+!![]' or '+[]' at position %d", index)
			}
		}

		if accept("+") {
			continue
		}
	}

	if !accept(")") {
		return 0, fmt.Errorf("expected ')' at position %d", index)
	}

	if index != len(expression) {
		return 0, fmt.Errorf("unexpected character at position %d", index)
	}

	return result, nil
}
rtxtxtrx 🤡🤡🤡🤡🤡
() автор топика
Последнее исправление: rtxtxtrx (всего исправлений: 1)
Ответ на: комментарий от Dorif

Так а примеры будут? self работает вполне просто - это просто переменная, в которую передается инстанс.

class Foo:
    def bar(self, value):
        self.qwerty = value


foo1 = Foo()
foo1.bar(10)
print(f"{foo1.qwerty=}")

foo2 = Foo()
Foo.bar(foo2, 20)
print(f"{foo2.qwerty=}")

Результат:

foo1.qwerty=10
foo2.qwerty=20

То же самое на go:

package main

import "fmt"

type Foo struct {
        bar int
}

func Bar1(foo *Foo, value int) {
        foo.bar = value
}

func (foo *Foo) Bar2(value int) {
        foo.bar = value
}

func main() {
        foo1 := Foo{}
        foo2 := Foo{}

        Bar1(&foo1, 10)
        foo2.Bar2(20)

        fmt.Println("foo1.bar =", foo1.bar)
        fmt.Println("foo2.bar =", foo2.bar)
}

Результат:

foo1.bar = 10
foo2.bar = 20

В обоих случаях у нас есть функция, которой передается в аргументы объект, с атрибутами которого нужно выполнить действие. И там и там есть сахар, позволяющий чуть удобнее вызывать такую функцию, но в целом это все так же функция, которой нужно передать объект. В питоне договорились называть этот аргумент self, но это не обязательно, это просто имя аргумента функции.

Соответственно у меня вопрос - как компилятор может понять что я работаю с каким-то определенным объектом если я никак его в функцию не передаю? Я бы хотел увидеть примеры того как это сделано в «неублюдочных» языках.

micronekodesu
()
Ответ на: комментарий от micronekodesu

это я говорю про скоуп только этой функции или имею ввиду атрибут объекта

А в питоне это как работает, если у тебя функция внутри функции? Так же и с объектами (должно быть).

no-such-file 👍👍👍👍👍
()
Ответ на: комментарий от no-such-file

То есть если у меня в объекте есть атрибут foo я не должен его использовать это имя в функции, а если использую то это значит я обращаюсь к атрибуту объекта?

В питоне переменная из внешней функции будет доступна для чтения, но если я попытаюсь в переменную с таким же именем что-то записать во внутренней функции то будет создан новый объект, а значение внешней переменной не изменится, если только я явно не укажу обратного (там есть global и nonlocal для такого). Если в языке все переменные «глобальные», их изменение в любом месте кода «легитимно», то разве это хорошо и не отличная возможность выстрелить в ногу?

micronekodesu
()
Ответ на: комментарий от micronekodesu

Я бы хотел увидеть примеры того как это сделано в «неублюдочных» языках.

Внимание, питону в пример ставится… пых!

class Foo {
    function bar(value) {
        this.qwerty = value
    }
}

$foo = new Foo();
$foo->bar(10);
print $foo->bar;

Просто на один self меньше function bar(value)

goingUp 😊😊😊😊😊
()
Ответ на: комментарий от goingUp

Спасибо! Но на мой взгляд тут особо нет разницы, все равно не компилятор как-то там магически определяет о какой переменной идет речь, а это надо явно указать, просто тут этот объект не надо передавать явно.

В питоне есть еще методы класса, куда передается не инстанс, а сам класс, есть статикметоды, в которых ни класс, ни инстанс не передаются, можешь показать как это работает в php?

class Foo:

    foo = 10

    @classmethod
    def bar(cls, value):
        cls.foo = value


foo1 = Foo()
foo2 = Foo()

print(f"{foo1.foo=}, {foo2.foo=}")

foo1.bar(20)

print(f"{foo1.foo=}, {foo2.foo=}")

Результат

foo1.foo=10, foo2.foo=10
foo1.foo=20, foo2.foo=20

Для этого в php тоже есть какой-то «неявный» вариант?

micronekodesu
()
Последнее исправление: micronekodesu (всего исправлений: 1)
Ответ на: комментарий от micronekodesu

Трудно набирать код одним пальцем. Можно посмотреть как в руби сделано: объект передается неявно, внутри метода все неквалифицированные вызовы трактуются как вызовы с self, для полей особый синтаксис. В питоне тоже так можно было бы, но там классы прикручены синей изолентой. И к этому еще идеология подбита, что явное лучше неявного. Хотя в других местах полно магии.

bread 😊😊😊😊
()

Итого: я все еще лучше AI…

Это некорректный вывод. Корректный: ты плохой промпт-инженер, но наверное не очень плохой погроммист :)

sergej
()
Ответ на: комментарий от bread

Можно посмотреть как в руби сделано

Я нашел вот такое, это оно? https://www.tutorialspoint.com/ruby/ruby_variables.htm

class Customer
   def initialize(id, name, addr)
      @cust_id = id
      @cust_name = name
      @cust_addr = addr
   end
   def display_details()
      puts "Customer id #@cust_id"
      puts "Customer name #@cust_name"
      puts "Customer address #@cust_addr"
   end
end

# Create Objects
cust1 = Customer.new("1", "John", "Wisdom Apartments, Ludhiya")
cust2 = Customer.new("2", "Poul", "New Empire road, Khandala")

# Call Methods
cust1.display_details()
cust2.display_details()

Результат

Customer id 1
Customer name John
Customer address Wisdom Apartments, Ludhiya
Customer id 2
Customer name Poul
Customer address New Empire road, Khandala
micronekodesu
()
Ответ на: комментарий от Gary

а к тому что класс передаётся два раза (в явном виде и в виде типа инстанса)

На сколько я могу судить по упомянутым ITT языкам везде так или иначе обращение к атрибутам объекта отличается от работы с «простыми» переменными. Просто где-то это сделали «слаще» и объект не передается в виде аргумента, а используется какое-то определенное слово\символ для доступа к объекту. Я понял «претензию» как будто есть языки где «обычные» переменные и атрибуты класса\объекта никак не отличаются по написанию, но компилятор сам понимает что имеется ввиду, мне было интересно как это работает. Если претензия только к тому что в питоне нужно писать self в параметрах каждой функции (потому что внутри функции так или иначе во всех примерах используются какие-то «спецсимволы» для доступа к неявно переданному объекту) то получается я понял не правильно.

micronekodesu
()
Ответ на: комментарий от micronekodesu

Я понял «претензию» как будто есть языки где «обычные» переменные и атрибуты класса\объекта никак не отличаются по написанию, но компилятор сам понимает что имеется ввиду, мне было интересно как это работает.

Это тоже кстати, про плюсы уже написали. В плюсах при обращении к полям инстанса внутри методов класса писать this не надо: https://godbolt.org/z/Ys5W79a3E

Gary ☕☕
()

и в итоге написал все сам

Всё Правильно Сделал. (c)

Итого: я все еще лучше AI…

А с другой стороны – вот в соседней теме человек рассказывает, как чат от DeepSeek ему с полпинка написал правильный работающий код не просто для не самого популярного языка, а конкретно для приложения этого языка к конкретной игре (и даже конкретной версии игры), где на этом языке моды пишутся. Таки, наверное, стоит потыкать палочкой. Не парсер, конечно…

P.S. Тему в Development, может, перекинуть? Я бы не сказал, что она «нетехническая», да и в трекере будет видна по умолчанию…

hobbit ☕☕☕☕☕
()
Последнее исправление: hobbit (всего исправлений: 5)
Ответ на: комментарий от micronekodesu

Да, есть

<?php

class Foo {
    static $qwerty;

    // статический метод
    static function bar($value) {
        static::$qwerty = $value;
    }

    // классметод - обычный метод, в обычном методе у нас есть доступ и к $this, и к self, и к static
    function classmethod($value) {
        static::$qwerty = $value;
    }
}

Foo::bar(123);
print Foo::$qwerty;

$foo = new Foo();
$foo->classmethod(456);
print Foo::$qwerty;
goingUp 😊😊😊😊😊
()
Ответ на: комментарий от goingUp

Нет, это вы наверное путаете с моделями DeepSeek, это проприетарная модель openAi недоступная для запуска на своем железе. Они ее спешно выкатили пару дней назад, чтобы клиенты не сбежали к китайцам.

На днях появились скриншоты, как новая модель от openAI случайно перешла на китайский :) Комментаторы писали, что эту модель спешно выкатили, чтобы перекрыть успех DeepSeek, и предполагали, что на DeepSeek и тренировали.

olegd
()

Только что попросил чтагпт сгенерить вот этот:

function isValidBrackets(str) {
    const stack = [];
    const bracketsMap = {
        ')': '(',
        '}': '{',
        ']': '[',
    };
    
    for (let char of str) {
        if (Object.values(bracketsMap).includes(char)) {
            stack.push(char);
        } else if (Object.keys(bracketsMap).includes(char)) {
            if (stack.pop() !== bracketsMap[char]) {
                return false;
            }
        }
    }
    
    return stack.length === 0;
}

// Примеры использования
console.log(isValidBrackets("+((+!+[]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]))")); // true
romanlinux
()